Hatena::Grouphaskell

eagletmtの日記 このページをアンテナに追加

|

2009-09-23

hs-plugins

15:03 | hs-plugins - eagletmtの日記 を含むブックマーク はてなブックマーク - hs-plugins - eagletmtの日記 hs-plugins - eagletmtの日記 のブックマークコメント

これの簡単な使い方とか.

http://hackage.haskell.org/package/plugins


load

典型的な使い方としてはまずインターフェイスを定義する.

-- API.hs
module API where

data Interface = Interface {
  fun :: String -> String
}

plugin :: Interface
plugin = Interface { fun = id } -- default implementation

プラグインのほうは

-- Plugin.hs
module Plugin where
import API

resource :: Interface
resource = plugin { fun = reverse }

のように書き,デフォルトの fun を reverse で上書きする.

メインのほうは

-- Main.hs
import System.Plugins
import API

main = do
  m <- load "Plugin.o" ["."] [] "resource"
  case m of
    LoadFailure msg -> print msg
    LoadSuccess _ v -> print $ (fun v) "hoge"

みたいに書く.

load の第一引数はオブジェクト,第二引数はオブジェクトを探しにいくディレクトリのリスト,第三引数は PackageConf (ここでは無視), 第四引数はシンボルの名前を指定する.


実行結果

% ghc -c API.hs
% ghc -c Plugin.hs
% runghc Main.hs
"egoh"

ここで,例えば Plugin.hs の resource を

resource = Interface { fun = filter (/= 'o') }

とかにして Plugin.o を再生成すれば,Main.hs を全く変えずに

% runghc Main.hs
"hge"

と動作を変えることができる.

dynload

Typeable と Dynamic を使っている.

-- API.hs
{-# LANGUAGE DeriveDataTypeable #-}
module API where

import Data.Typeable

data Interface = Interface {
  fun :: String -> String
} deriving Typeable

plugin :: Interface
plugin = Interface { fun = id }
-- Plugin.hs
module Plugin where

import API
import Data.Dynamic

resource :: Dynamic
resource = toDyn $ plugin { fun = filter (/='o') }
-- Main.hs
import System.Plugins
import API

main = do
  m <- dynload "Plugin.o" ["."] [] "resource"
  case m of
    LoadFailure msg -> print msg
    LoadSuccess _ v -> print $ (fun v) "hoge"

pdynload

実行時に GHC を使って型チェックしてるっぽい.

-- API.hs
module API where

data Interface = Interface {
  fun :: String -> String
}

plugin :: Interface
plugin = Interface { fun = id }
-- Plugin.hs
module Plugin where

import API

resource :: Interface
resource = plugin { fun = filter (/='o') }
-- Main.hs
import System.Plugins
import API

main = do
  m <- pdynload "Plugin.o" ["."] [] "API.Interface" "resource"
  case m of
    LoadFailure msg -> print msg
    LoadSuccess _ v -> print $ (fun v) "hoge"

pdynload の第四引数は第五引数のシンボルが持つべき型を書く.

しかし

ちょうど HaskellML に hs-plugins に関することが流れていた.

hs-plugins isn't particularly maintained right now; you should

probably use hint instead, or use the GHC API directly.

http://www.haskell.org/pipermail/haskell/2009-September/021633.html

どうりでドキュメントが古いわけだ…

http://hackage.haskell.org/package/hint

hint の使い方

03:02 | hint の使い方 - eagletmtの日記 を含むブックマーク はてなブックマーク - hint の使い方 - eagletmtの日記 hint の使い方 - eagletmtの日記 のブックマークコメント

まぁhint の公式ページにもサンプルがあるので,敢えて紹介するほどではないけど.

-- Plugin.hs
module Plugin where

fun :: String -> String
fun = reverse
-- Main.hs
import Language.Haskell.Interpreter

main = do
  r <- runInterpreter interp
  case r of
    Left err -> print err
    Right fun -> print $ fun "hoge"

interp :: Interpreter (String -> String)
interp = do
  loadModules ["Plugin.hs"]
  setTopLevelModules ["Plugin"]
  interpret "fun" (as :: String -> String)

実行結果

% ghc --make Main.hs
% ./Main
"egoh"

ところで上の ghc --make したときに本当は

ld warning: atom sorting error for _ghczm6zi10zi4_LibFFI_Czuffizutype_closure_tbl and _ghczm6zi10zi4_LibFFI_Czuffizucif_closure_tbl in /Library/Frameworks/GHC.framework/Versions/610/usr/lib/ghc-6.10.4/ghc-6.10.4/libHSghc-6.10.4.a(LibFFI.o)
ld warning: atom sorting error for _ghczm6zi10zi4_LibFFI_Czuffizutype_closure_tbl and _ghczm6zi10zi4_LibFFI_Czuffizucif_closure_tbl in /Library/Frameworks/GHC.framework/Versions/610/usr/lib/ghc-6.10.4/ghc-6.10.4/libHSghc-6.10.4.a(LibFFI.o)

が出たんだけど何なんだろう…

runghc Main.hs したときには出ないんだけど.

JuniorJunior2013/03/30 01:30Felt so hoepesls looking for answers to my questions...until now.

xahjemwxahjemw2013/03/30 16:21O44TbI <a href="http://elvurwymwqsj.com/">elvurwymwqsj</a>

yrdmsxesysyyrdmsxesysy2013/03/31 23:402daHtz , [url=http://tncrszsqkhtg.com/]tncrszsqkhtg[/url], [link=http://idvidbfyvtur.com/]idvidbfyvtur[/link], http://vckdlyyfyqkc.com/

トラックバック - http://haskell.g.hatena.ne.jp/eagletmt/20090923

2009-09-01

ComposeF を修正

21:13 | ComposeF を修正 - eagletmtの日記 を含むブックマーク はてなブックマーク - ComposeF を修正 - eagletmtの日記 ComposeF を修正 - eagletmtの日記 のブックマークコメント

http://haskell.g.hatena.ne.jp/eagletmt/20090831/1251729918

昨日の ComposeF はちょっと間違っていたので修正.

data ComposeF f g = ComposeF f g
instance (Apply g x x', Apply f x' x'') => Apply (ComposeF f g) x x''

この順番のほうが自然ですよね…

ComposeF といちいち書くのがだるいので,演算子を定義してみる.

f ... g = ComposeF f g
infixr 9 ...

動作テストなど.

class Add x y z | x y -> z where
  add :: x -> y -> z
  add = undefined
instance Add Zero y y 
instance Add x y z => Add (Succ x) y (Succ z)
data AddF x = AddF x
instance (Add x y z) => Apply (AddF x) y z 

class Mult x y z | x y -> z where
  mult :: x -> y -> z
  mult = undefined
instance Mult Zero y Zero
instance (Mult x y z, Add y z r) => Mult (Succ x) y r
data MultF x = MultF x
instance Mult x y z => Apply (MultF x) y z

two :: Two
two = undefined

composeOk :: Cons Ten (Cons Six Nil) -> String
composeOk = const "compose ok"

main = putStrLn $ composeOk $ map (MultF two ... AddF two ... SuccF) (undefined :: Cons Two (Cons Zero Nil))
トラックバック - http://haskell.g.hatena.ne.jp/eagletmt/20090901

2009-08-31

型リストに対する map

20:31 | 型リストに対する map - eagletmtの日記 を含むブックマーク はてなブックマーク - 型リストに対する map - eagletmtの日記 型リストに対する map - eagletmtの日記 のブックマークコメント

最初はこんな感じに書いた

class Apply f x y | f x -> y where
  apply :: f -> x -> y
  apply = undefined
instance Apply (x -> y) x y

class Map f xs ys | f xs -> ys where
  map :: f -> xs -> ys
  map = undefined
instance Map f Nil Nil 
instance (Apply f x y, Map f xs ys) => Map f (Cons x xs) (Cons y ys)

しかし,この Apply がうまくいってくれない.

*Main> :t apply Succ Zero
apply Succ Zero :: (Apply (n -> Succ n) Zero y) => y

えー,なんでこれが Succ Zero にならないの.


ということを Twitter でつぶやいたら,@keigoi さんにこのような方法を教えてもらった.

@lazyeagle んー Apply (x -> y) x y ではなく Apply SuccF x (Succ x) みたいな SuccF はただのタグ

http://twitter.com/keigoi/status/3620716095

あと instance x'~x => Apply SuccF x (Succ x') みたいなことはよくやる むかしは TypeCast使ってたけど

http://twitter.com/keigoi/status/3620726862

後者はよくわからなかった*1ので,前者を試してみた.

data SuccF = SuccF
instance Apply SuccF x (Succ x)
*Main> :t apply Succ Zero
apply SuccF Zero :: Succ Zero

おー,できたできた.

*Main> let ls = undefined :: Cons Three (Cons One (Cons Two Nil))
*Main> :t map SuccF ls
map SuccF ls
  :: Cons
       (Succ (Succ Two))
       (Cons (Succ (Succ Zero)) (Cons (Succ (Succ One)) Nil))

map もちゃんとできてる.


ただ,これだとちょっと残念なことがあって,例えば2つ足す場合,

data Succ2F = Succ2F
instance Apply Succ2F x (Succ (Succ x))

と,わざわざ書かなければならない.

最初のコードがうまくいってくれれば,

apply (undefined :: n -> Succ (Succ n)) Zero :: Succ (Succ Zero)

となってくれると思うのに.


後者の方法についてはとりあえず TODO としておくことにします…

型レベルの関数合成?

23:45 | 型レベルの関数合成? - eagletmtの日記 を含むブックマーク はてなブックマーク - 型レベルの関数合成? - eagletmtの日記 型レベルの関数合成? - eagletmtの日記 のブックマークコメント

型レベル高階関数 (?) - keigoiの日記

なるほど,そういう発想でいけばいいのか.ありがとうございます.


それなら DoubleF のような限定的なものより,ちょっと変えてもっと広く使えそうな ComposeF のほうがいいと思った.

data ComposeF f g = ComposeF f g 
instance (Apply f x x', Apply g x' x'') => Apply (ComposeF f g) x x''

試しに3足してみる.

*Main> :t apply (ComposeF SuccF (ComposeF SuccF SuccF)) Zero
apply (ComposeF SuccF (ComposeF SuccF SuccF)) Zero
  :: Succ (Succ (Succ Zero))
*Main> let ls = undefined :: Cons One (Cons Zero Nil)
*Main> :t map (ComposeF SuccF (ComposeF SuccF SuccF)) ls
map (ComposeF SuccF (ComposeF SuccF SuccF)) ls
  :: Cons
       (Succ (Succ (Succ (Succ Zero))))
       (Cons (Succ (Succ (Succ Zero))) Nil)

*1:~ が使われているから,今日読んだ Type Families の話かなぁ…というか読んでもよくわからなかった…

keigoikeigoi2009/08/31 21:23~ の話は、Apply とは関係ありませんでした,ごめんなさい。
~ は, Type Families で追加された、型の等価性の制約です. t ~ t' で, 型 t と t' は同じ型である,と読めばいいと思います

これを型レでどう使うかといえば,たとえばMPTCとインスタンス
class C a b
instance C (T a) (U a)
があるとき, 型 T a と 型 U b は C のインスタンスにはなりません.が,
instance a~a' => C (T a) (U a')
としておけば,T a と U b はCのインスタンスになり, 型変数 aとbが単一化されます.

インスタンスヘッドに型変数が複数回出てくるときに,場合によっては ~ を使えばより多くの型をMPTCのインスタンスにできることがある, というテクニックでした.
テキトーにぼやいただけじゃわかんないですよね,申し訳ない.

keigoikeigoi2009/08/31 21:25あと,2つ足すやつは…
succ2 = applu SuccF . apply SuccF
とかじゃダメですかね。

トラックバック - http://haskell.g.hatena.ne.jp/eagletmt/20090831

2009-08-27

型リストをソートする (MPTC とか fundeps とかそのへん)

15:54 | 型リストをソートする (MPTC とか fundeps とかそのへん) - eagletmtの日記 を含むブックマーク はてなブックマーク - 型リストをソートする (MPTC とか fundeps とかそのへん) - eagletmtの日記 型リストをソートする (MPTC とか fundeps とかそのへん) - eagletmtの日記 のブックマークコメント

Multi-Parameter Type Classes (MPTC) や Functional Dependencies (fundeps) について調べていた.

まず MPTC や fundeps とは何かというのはここの一番下にある資料が役に立った.

404 Not Found

後半の型推論の話は正直に言って全然よくわからなかったけど…


次に Haskell Wiki の fundeps のページの Tutorial にある Fun with Functional Dependencies を読んだ.

no title

Fun with Functional Dependencies

ここには fundeps の活用の仕方が書かれていて,とても面白かった.

最後に紹介されていた型リストを挿入ソートするコードが特に面白そうに思えたので,がんばってマージソートバージョンを書いてみた.

merge sort - Haskell - Snipplr Social Snippet Repository

Split のあたりがかなり汚ない点など改善の余地は十分あると思うが,一応書けた.

きちんとソートされているかの確認方法がよくわからなかったので,適当に ok という関数を作ってテストしてみたけど,こんなかんじでいいのだろうか…?

トラックバック - http://haskell.g.hatena.ne.jp/eagletmt/20090827

2009-08-26

MonadError

16:31 | MonadError - eagletmtの日記 を含むブックマーク はてなブックマーク - MonadError - eagletmtの日記 MonadError - eagletmtの日記 のブックマークコメント

メモ程度に.

import System.IO (openFile, hClose, IOMode(ReadMode))
import Control.Monad.Error (catchError, throwError)

exist :: FilePath -> IO Bool
exist path = (openFile path ReadMode >>= hClose >> return True) `catchError` const (return False)

foo, bar, hoge :: Either String Int
foo = throwError "foo"
bar = Right 10
hoge = Right 20

main = do
  exist "/etc/passwd" >>= print
  exist "/does/not/exist" >>= print
  print $ foo >> bar `catchError` Left
  print $ bar >> foo `catchError` Left
  print $ bar >> hoge `catchError` Left

実行結果

True
False
Left "foo"
Left "foo"
Right 20

ちなみに exist のような役割として,System.Directory に doesFileExist や doesDirectoryExist があります.


参考

Control.Monad.Error.Class

トラックバック - http://haskell.g.hatena.ne.jp/eagletmt/20090826
|