[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

で調べた。



OSXmysqlというとHDBC-mysqlの時に

こんな問題あったけどこれは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)で数値リテラルをアノテートなどして用います。
JavaBigDecimalに相当するものでしょうか?(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

うーん有理数経由なので正確でしょうけど。

オブジェクト指向から理解する型クラス

Haskell Advent Calendar2011 2日目です。

もう42時になってしまいました...さすがに遅いですね。


という目論見の元にスライドを作りましたが、ユーザ視点が足りずに混乱させてしまったかも知れません。



Haskell Advent Calendar明日はid:melponさんです。

続・Template Haskell入門 -- QuasiQuotes編

先にこちらをどうぞ。Template Haskell入門 - think and error


QuasiQuotesで簡単なものを作りましょう。
ここではヒアドキュメントを作ろうと思います。Haskellにはヒアドキュメントがないですから。


参考:


前提:

  • 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に等しいそうです。

一応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.

確かに通ります。