forall とか 型クラスとか

勉強会(http://www.agusa.i.is.nagoya-u.ac.jp/person/mzp/hiki/?SICP)で、S 式ネイティブコンパイラを作ろうぜ、ということだったので、id:mzpid:suer さんに手伝ってもらってちょっと作ってみてた。
前々から一度 forall を使ってみたかったのと、数値はひとつのデータコンストラクタで扱いたかったので、こんな感じに。Real とか Rational とかの関係がよくわからんくて無駄に時間がかかった。

{-# OPTIONS -fglasgow-exts #-}
data LispNum = forall a. (Real a, Show a) => Number a
instance Show LispNum where
    show (Number n) = show n
instance Eq LispNum where
    (Number x) == (Number y) = toRational x == toRational y
instance Num LispNum where
    (Number x) + (Number y) =
        Number . fromRational $ toRational x + toRational y
Main> Number 1 + Number 2
3.0
Main> Number 1 + Number 2.5
3.5
Main> 

Number は、本来は

data LispVal = forall a. (Real a, Show a) => Number a
            | String String
            | Boolean Bool
            | ...

みたいに LispVal のうちのひとつとして使いたかったので、

data LispVal a = Number a
...

とかはやりたくなかった。いろいろ問題が発生して投げやりな感じに…。

というか、今思ったけどこれって全然ダメじゃね。単に Real な値を保持してるだけでは…。

んー?

data LispNum = LInt Int | LReal Rational
instance Show LispNum where
    show (LInt i)  = show i
    show (LReal r) = show $ fromRational r
instance Eq LispNum where
    (LInt  i) == (LInt  j) = i==j
    (LReal r) == (LReal s) = r==s
    (LInt  i) == (LReal r) = toRational i == toRational r
    (LReal r) == (LInt  i) = toRational r == toRational i
instance Num LispNum where
    (LInt  i) + (LInt j ) = LInt  $ i+j
    (LReal r) + (LReal s) = LReal $ r+s
    (LInt  i) + (LReal r) = LReal $ toRational i + toRational r
    (LReal r) + (LInt  i) = LReal $ toRational i + toRational r