[Haskell]Haskellで文字列をevalするというかhintパッケージ
文字列からevalする感じのライブラリです。
GHCAPIのラッパーとも言います。
えーと。いや、使い方ずっと分かってなかったというか。
Preludeのimportが必要だったのだね..
import qualified Language.Haskell.Interpreter as I main = do line <- getLine result <- evalStr line case result of Right r -> print r Left er -> case er of I.UnknownError e -> putStrLn e I.WontCompile es -> mapM_ print es I.NotAllowed e -> putStrLn e I.GhcException e -> putStrLn e evalStr str = I.runInterpreter $ do I.setImports ["Prelude"] I.eval str
ふむふむ。
[haskell]OSX Lionでcabal install mysql
メモ。
cabal install mysql(現最新は0.1.1.4)でエラーが出る。
ghc7.0.4。
じゃあと思って
$ cabal install mysql-simple $ cabal install persistent-mysql
してみたけどmysqlに依存してるぽい。
仕方ない。
$ cabal install --extra-lib-dirs=/usr/local/mysql/lib/ --extra-include-dirs=/usr/local/mysql/include/ mysql Resolving dependencies... [1 of 1] Compiling Main ( /var/folders/ym/jf63kw2n6453l1wq7s_186rr0000gn/T/mysql-0.1.1.423059/mysql-0.1.1.4/Setup.lhs, /var/folders/ym/jf63kw2n6453l1wq7s_186rr0000gn/T/mysql-0.1.1.423059/mysql-0.1.1.4/dist/setup/Main.o ) Linking /var/folders/ym/jf63kw2n6453l1wq7s_186rr0000gn/T/mysql-0.1.1.423059/mysql-0.1.1.4/dist/setup/setup ... ld: warning: could not create compact unwind for _ffi_call_unix64: does not use RBP or RSP based frame Configuring mysql-0.1.1.4... Preprocessing library mysql-0.1.1.4... dyld: Library not loaded: libmysqlclient.18.dylib Referenced from: /private/var/folders/ym/jf63kw2n6453l1wq7s_186rr0000gn/T/mysql-0.1.1.423059/mysql-0.1.1.4/dist/build/Database/MySQL/Base/C_hsc_make Reason: image not found running dist/build/Database/MySQL/Base/C_hsc_make failed (exit code 5) command was: dist/build/Database/MySQL/Base/C_hsc_make >dist/build/Database/MySQL/Base/C.hs cabal: Error: some packages failed to install: mysql-0.1.1.4 failed during the building phase. The exception was: ExitFailure 1
エラー内容に
dyld: Library not loaded: libmysqlclient.18.dylib
とあるので、 --extra-lib-dirs, --extra-include-dirs使えば良いかなーと思ったのだけどその結果が上。エラー内容変わらず。
libmysqlclient.18.dylibの場所は
$ mdfind libmysqlclient.18.dylib /usr/local/mysql-5.5.14-osx10.6-x86_64/lib/libmysqlclient.18.dylib
で調べた。
こんな問題あったけどこれは7.0.2あたりで直っているはず。
ということで
ld: warning: could not create compact unwind for _ffi_call_unix64: does not use RBP or RSP based frame
これでグーグルさんに聞いてみると、
こんなバグが。milestone 7.6.1かー。
しようがないしよくわからないのでこれはスルーして
dyld: Library not loaded: libmysqlclient.18.dylib
このへんで検索。こんなのがヒット。
railsでの件だけど同じ問題が出ているらし。
install_name_toolsとかよくわからないものは取りあえず使わない方向で、次を実行。
$ export DYLD_LIBRARY_PATH=/usr/local/mysql/lib:$DYLD_LIBRARY_PATH
そしてcabal install mysql。通った!
cabal install --extra-lib-dirsあたりのオプションでは駄目なのだろうか...
この辺のオプションは
$ cabal install --help
でずらずら出てくる。
[haskell]Haskellで任意精度小数を扱う
Data.Fixedモジュールを用います。
単純に使うだけなら、モジュールをimportして具体的な精度をもつ型(Uni, Deci, Centi, Milli, Micro, Nano, Pico)で数値リテラルをアノテートなどして用います。
JavaのBigDecimalに相当するものでしょうか?(Javaは良く知りませんが..)
import Data.Fixed (Pico) main = print (1.2345678 + 3.00000012345 :: Pico) -- 1e-12精度での演算
Data.Fixedを見てみると、3つの関数(div', mod', divMod')と、
任意精度小数のための型コンストラクタFixed, 型クラスHasResolution, 表示用関数showFixed,
具体的な精度の型等exportされています。
module Data.Fixed ( div',mod',divMod', Fixed,HasResolution(..), showFixed, E0,Uni, E1,Deci, E2,Centi, E3,Milli, E6,Micro, E9,Nano, E12,Pico ) where
自分で具体的な精度を持つ型を作成することも出来ます。
ここでは小数点以下60桁の精度を持つ型(TooPrecise)を作ってみます。
import Data.Fixed data E60 = E60 instance HasResolution E60 where resolution _ = 1000000000000000000000000000000000000000000000000000000000000 type TooPrecise = Fixed E60 main = do print (3.0000000000000000000000000003 :: TooPrecise) print $ sum [0.1 :: TooPrecise, 0.2 .. 0.4]
何故任意精度の小数を誤差無しに扱えるのでしょうか。
(Fixed a)型の内部では具体的な数値と精度をIntegerとして別々に保持しているからです。
例えば(0.12::Fixed E3)は(120::Integer)と(1000::Integer)という2つの数値を持っている訳ですね。
なので計算自体はInteger同士で行うためコストは高く付きますが、精度内なら誤差なく計算が出来るということです。
単に型アノテートするだけで使えるのは、Haskellの数値リテラルが多相的だからですね。
型の相互変換にはtoRationalとfromRationalでしょうか。
changeResolution :: (HasResolution a, HasResolution b) => Fixed a -> Fixed b changeResolution = fromRational . toRational pico2nano :: Pico -> Nano pico2nano = changeResolution
うーん有理数経由なので正確でしょうけど。
Programming Haskell Chapter8
スタートHaskell第5回あたりで発表した資料です。
プログラミングHaskell8章で引っかかる人の助けにでも鳴りましたら。
オブジェクト指向から理解する型クラス
Haskell Advent Calendar2011 2日目です。
もう42時になってしまいました...さすがに遅いですね。
- Haskellと言えば型クラス
- オブジェクト指向のクラスとHaskellの型クラスは違いますよ的な説明は見ますがどう違うか比べた情報が無い
- オブジェクト指向知っている人からの理解を簡単にすればHaskell理解する人が増えますね!
という目論見の元にスライドを作りましたが、ユーザ視点が足りずに混乱させてしまったかも知れません。
続・Template Haskell入門 -- QuasiQuotes編
先にこちらをどうぞ。Template Haskell入門 - think and error
QuasiQuotesで簡単なものを作りましょう。
ここではヒアドキュメントを作ろうと思います。Haskellにはヒアドキュメントがないですから。
参考:
- こんさんのあれ:準クォートでもてかわゆるふわメタプログラミング! - はてな使ったら負けだと思っている deriving Haskell - haskell
- TemplateHaskellのあれ
前提:
- GHC7.2.1
そもそもTemplateHaskellで何が出来るのか
「既知の情報」と「ルール」をもとにHaskellのコードを生成するわけですね。
Haskellのコードで直接実現出来ないことや、似たようなコードを大量に書きたい時など便利そうです。
例えば可変引数関数(QuasiQuotesの例としてprintfが挙げられていますね)や、タプル関連の汎用関数とか、大量のインスタンス宣言とかでしょうか。
QuasiQuoter
オレオレQuasiQuotesを作るにはQuasiQuoterを定義すればよいです。それだけです。
heredoc = QuasiQuoter { quoteExp = parseExp :: String -> Q Exp , quotePat = undefined :: String -> Q Pat , quoteType = undefined :: String -> Q Type , quoteDec = undefined :: String -> Q [Dec] }
4つのフィールドはTemplate HaskellのOxford Bracketsのe, p, t, dに対応しているようです。
つまり作成したQuasiQuoterをExpressionの箇所に置くとquoteExpが使用され、Patternの箇所に置けばquotePatが使用される...と言った具合ですね。HereDocumentはexpressionとしてしか使わないため、quoteExp以外はundefinedとしておきます。
runQ
Quoteて種類が沢山あるため、調べるのが面倒ですよね。ということで作りたいHaskellコードのQ表現が知りたいときにrunQを使います。
ghci> runQ [d| main = putStrLn "Hello, Haskell!" |] [ValD (VarP main_21) (NormalB (AppE (VarE System.IO.putStrLn) (LitE (StringL "Hello, Haskell!")))) []]
というのもQはShowのインスタンスではないため、文字列に直に変換出来ないためのようです。
このとき表示される値コンストラクタはVarE, VarPなど、大文字から始まっていますが、実際に使う時は小文字から始まる関数を用います。
{-# LANGUAGE TemplateHaskell, QuasiQuotes #-} import Language.Haskell.TH do mainQ <- valD (varP $ mkName "main") (normalB (appE (varE 'putStrLn) (litE (stringL "Hello, Haskell!")))) [] return [mainQ]
トップレベル宣言(Q [Dec])の場合は$()を省略出来るのでした。main関数が無いですが、そのmain関数をTemplate Haskellで生成しているため問題ありません。このまま実行出来ます。
valDはDecQ(Q Decのシノニム)を返すので、Q [Dec]に変換しています。QはFunctorのインスタンスなのでfmap使っても良いですが。
ヒアドキュメント作成
ということでString -> ExpQとなるパーサを書いてあげればよいわけです。
今回はヒアドキュメントなので、ExpQの部分は文字列のQ表現になれば良い訳です。
ということで調べてみると、
ghci> runQ [|"aaa"|] LitE (StringL "aaa")
こんなかんじですね。
そのためOxford Bracketsの中の文字列をそのまま返すだけであるなら、QuasiQuoterを以下のように設定すればいいわけです。
doNothing :: QuasiQuoter doNothing = QuasiQuoter { quoteExp = litE . stringL , quotePat = undefined , quoteType = undefined , quoteDec = undefined }
実行すると以下のようになります。
ghci> [doNothing| aaa bbb |] " aaa bbb "
できました。しかし折角HereDocumentを作るのなら、変数を文字列として埋め込みたいですね。
antiQuotesは誰でも何となくわかるPHP風としましょう。$var, {$var}とかいった記述です。
アンチクオート置き換え部分は正直まだよくわかってないです。特にdataToExpQが具体的に何しているかわからんです。
とりあえずこんさんのJSONのアレを参考に適当に修正して作りました。
{-# LANGUAGE TemplateHaskell, DeriveDataTypeable, OverlappingInstances, OverloadedStrings #-} {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} module HereDoc (heredoc) where -- Template Haskell import Language.Haskell.TH import Language.Haskell.TH.Quote import Data.Generics -- Parser import Data.Attoparsec.Text import Data.Text.Internal (Text) import qualified Data.Text as T import Control.Applicative hiding (many) -- General purpose import Control.Monad import Data.Maybe -- 目的のQuasiQuoter heredoc :: QuasiQuoter heredoc = QuasiQuoter { quoteExp = parseExp , quotePat = undefined , quoteType = undefined , quoteDec = undefined } data HereDoc = HDString String | HDQuote String | HDList [HereDoc] -- うーんスマートじゃない。 deriving (Show, Eq, Data, Typeable) -------------------------------------------------------------------------------- hdString = HDString . T.unpack <$> takeWhile1 (notInClass "${") hdQuote = HDQuote . T.unpack <$> (curlyBraced antiQuoteSymbol <|> antiQuoteSymbol) -- {,$あたりが含まれていてantiQuoteでは無い部分をHDStringとして消費。あんまりスマートじゃないけど。 notHdQuote = HDString . T.unpack <$> (string "$" <|> string "{") antiQuoteSymbol = string "$" *> (liftM T.pack $ many1 (letter <|> digit <|> char '_')) -- attoparsecのこの演算子よいね。 curlyBraced p = "{" .*> p <*. "}" -- 適当にtryべたべた parserHereDoc = many (try hdQuote <|> try notHdQuote <|> try hdString) -------------------------------------------------------------------------------- parseQa :: (HereDoc -> Q a) -> String -> Q a parseQa heredocToA str = do case parseOnly parserHereDoc (T.pack str) of Right hd -> heredocToA $ HDList hd Left _ -> error "err" -- 良く分かってない parseExp :: String -> ExpQ parseExp = parseQa (dataToExpQ $ const Nothing `extQ` antiQuoteE) -- 良く分かってない antiQuoteE :: HereDoc -> Maybe ExpQ antiQuoteE (HDQuote nm) = Just $ {-appE (varE 'show)-} (varE $ mkName nm) antiQuoteE (HDString nm) = Just $ litE (stringL nm) antiQuoteE (HDList hds) = Just $ appE (varE 'concat) (listE $ mapMaybe antiQuoteE hds) -- mapMaybeおかしい
現在antiQuoteとしてStringしか埋め込めません。
Showのインスタンスならshowして埋め込み、とかしたかったのだけど、そうすると肝心のStringをshowした時にダブルクォートが表示されてしまってよくない..
まあうまく処理出来なかったのでそのうち。
ghci> :load HereDoc.hs ghci> :set -XTemplateHaskell -XQuasiQuotes ghci> let aaa = "AAA"; bbb = "BBB"; ccc = "CCC" ghci> [heredoc|{$aaa}hoge$bbb{$ccc}|] "AAAhogeBBBCCC" ghci> let name = "ruicc"; lang = "Haskell" ghci> [heredoc|Hey $name, how are you doing in $lang?|] "Hey ruicc, how are you doing in Haskell?"
一応問題なくうごいているようですね。
Haskellの関数合成でひっかかってたこと
関数合成てのは
f(g(x))
こんなやつです。でもたまには次のようにやりたくなるわけですね。
h(x,y) = f(g(x,y))
Haskellでの関数合成は(.)です。これで上記をやろうとします。
-- 1引数関数 f x = x + 2 -- 2引数関数 g x y = x * y -- fとgを合成しようとする h = f . g -- hに2引数渡そうとする main = print $ h 3 5 -- => 17?
これは型エラーがでます。
No instance for (Num (a0 -> a0)) arising from a use of `f' Possible fix: add an instance declaration for (Num (a0 -> a0)) In the first argument of `(.)', namely `f' In the expression: f . g In an equation for `h': h = f . g
(a0 -> a0)だと?
...なんだこれ。
このエラーが出る度、仕方なく(.)の代わりに渋々($)で書き直していたりしていたわけですが、先日漸く理由がわかりました。
gは2引数関数として定義したつもりだけど、Haskellでは関数は全てCurry化されているため、そんなもの(2引数関数)はそもそも存在しないから。
そういえばそうでした。
つまりfと合成したgは、
f x = x + 2 g x = \y -> x * y h = f . g -- \x -> ((\y -> x * y) + 2) -- 型エラー!
こうやって1引数関数と解釈され、合成されていたんですね。実際には演算子(+),(*)の使用上、合成出来ずに上記型エラーが出ているわけですが。
初めの数式の通りに2引数関数と1引数関数を合成したいときは...一回uncurryでもしますか。
h = curry $ f . uncurry g main = print $ h 3 5 -- => 17
こうするとhは意図通り2引数関数(面倒だからそう呼ぶ)になります。
追記
@maoeさんと@cutseaさんに、もっと簡潔に書けると教えて頂きました。
h = (f.).g
型を調べてみます。
ghci> :type (f.) (f.) :: Num c => (a -> c) -> a -> c ghci> :type (f.).g (f.).g :: Num c => c -> c -> c
確かに2引数になっています。
更に多い引数を合成する場合は、(.)のセクションを増やせば良いようですね。
-- 1引数 f x = x + 2 -- 2引数 g x y = x * y -- 3引数 g' x y z = x * y * z -- 4引数 g'' x y z w = x * y * z * w -- 3引数と1引数の合成 h' = ((f.).).g' -- 4引数と1引数の合成 h'' = ((f.).).).g''
まあそこまですることはあまりないかもですが。
追記2: Arrowへの導入?
この関数合成の流れでArrowへの導入とすると綺麗でしょうか。僕Arrow良く分かってないですが。
返り値を2つ返す関数と、2引数関数の合成です。
import Control.Arrow -- 2引数 f x y = x - y -- 1引数で返り値2つ(Tupleで返す) g x = (x * 2 , x + 2) -- gの左側 gl x = x * 2 -- gの右側 gr x = x + 2 -- 返り値を2つ返す関数(g)と2引数関数(f)の合成 h = uncurry f <<< g -- hと同じ h' = uncurry f <<< gl &&& gr main = do print $ h 4 -- (4 * 2) - (4 + 2) => 2 print $ h' 4 -- (4 * 2) - (4 + 2) => 2
バグが入らない小さな関数を組み合わせてプログラミング。
高階関数でひっかかってたこと
関数合成関係ないですがついでに。高階関数つながりで。
constとidはそれぞれ以下のような型を持ちます。
ghci> :type const const :: a -> b -> a ghci> :type id id :: a -> a
そしてconstの第一引数にidを渡すと、
ghci> :type const id const id :: b -> a -> a
こうなります。
これがどうなっているのか分からず、しばらく悩みました。
色々試した結果、どうやら
const :: a -> b -> a
この型パラメータ'a'に idの型(c -> c)を入れると導出できるようですね。
-- とりあえずaの部分に(c->c)を突っ込んでみる const :: (c -> c) -> b -> (c -> c) -- ??
第一引数はidを渡すので、実際には左の(c -> c)部分は消費されます。よって、
const id :: b -> (c -> c)
(->)は右結合なので
const id :: b -> c -> c
となると。
const idはflip constに等しいそうです。
- 参考:2009-12-08
一応QuickCheckで試してみます。
Prelude> :m Test.QuickCheck Prelude Test.QuickCheck> let prop_const x y = flip const x y == const id x y Prelude Test.QuickCheck> quickCheck (prop_const :: Int -> Int -> Bool) +++ OK, passed 100 tests.
確かに通ります。