Dynamicな事をしだした

RubyからHaskellのコードに変換(別にRubyじゃなくてDynamicな言語で)するのってどうしてやろうとかいう事を考えていたので、愚直にData.Dynamicはどうだろうかとか。


HaskellのData.Dynamicは、取りあえず値をDynamicで包んで、後で取り出してあげましょうという感じですが、良い感じで駄目な気がする。

Prelude> :m Data.Dynamic
Prelude Data.Dynamic> toDyn "hoge"
Prelude Data.Dynamic> fromDyn (toDyn "hoge") "default-value"
"hoge"

*Main> let hoge (Just a) = a
*Main> hoge (fromDyna (toMDyn "hoge")) ++ "bar"
"hogebar"
*Main> hoge (fromDyna (toMDyn "hoge")) + 2
*** Exception: <interactive>:1:4-20: Non-exhaustive patterns in function hoge

http://haskell.org/ghc/docs/latest/html/libraries/base/Data-Dynamic.html
ここを見ればわかる話なんですけど。


fromDynの中身は

fromDyn (Dynamic t v) def
  | typeOf def == t = unsafeCoerce v
  | otherwise       = def

Dynamicは、型と値を保持しておくdata constructorで、その定義は

data Dynamic = Dynamic TypeRep Obj

#ifdef __GLASGOW_HASKELL__
type Obj = Any
 -- Use GHC's primitive 'Any' type to hold the dynamically typed value.
 --
 -- In GHC's new eval/apply execution model this type must not look
 -- like a data type.  If it did, GHC would use the constructor convention 
 -- when evaluating it, and this will go wrong if the object is really a 
 -- function.  Using Any forces GHC to use
 -- a fallback convention for evaluating it that works for all types.

こんな感じになってくる。
が、私は日本人なのでアメリカ語とかは読めない、日本語でおk。取りあえず何でも受け取れる感じにしたいんだから、バッドノウハウで習得した次の様なのを入れる。

{-# LANGUAGE EmptyDataDecls,RankNTypes #-}

{-
MyAny1と同じ定義のAnyは、Data.Monoidの中にあって
http://haskell.org/ghc/docs/6.8.3/html/libraries/base/Data-Monoid.html
newtype Any

Anyの定義がもう1つあるのは、
http://haskell.org/ghc/docs/6.8.3/html/libraries/base/GHC-Prim.html
data Any a
ただ後者の定義では上手く動かない。
-}

data MyAny1
type MyAny2 = forall a . a

として、Data.Dynamicと全く同じですが

{-# LANGUAGE EmptyDataDecls,RankNTypes #-}
-- import GHC.Base
-- import Data.Dynamic
-- import Data.Monoid
import Data.Typeable
import Unsafe.Coerce


data MyAny1
type MyAny2 = forall a . a

data MyDyn = MyDyn TypeRep MyAny2

toMDyn v = MyDyn (typeOf v) (unsafeCoerce v)

-- 返す型の値がdefで決められるから、unsafeCoerceでそっちにcastされる
fromDyn (MyDyn t v) def
    | typeOf def == t = unsafeCoerce v
    | otherwise       = def

-- 実行時に、どの型にキャストすべきかをunsafeCoerceに任せる
fromDyna (MyDyn t v) = 
    case unsafeCoerce v of
      r | t == typeOf r -> Just r
        | otherwise     -> Nothing

formDynaの例は上に示してますが、例えば

Prelude Data.Char> :t ord
ord :: Char -> Int
Prelude Data.Char> ord 'a'
97
Prelude Data.Char> ord 0

<interactive>:1:4:
    No instance for (Num Char)
      arising from the literal `0' at <interactive>:1:4
    Possible fix: add an instance declaration for (Num Char)
    In the first argument of `ord', namely `0'
    In the expression: ord 0
    In the definition of `it': it = ord 0
Prelude Data.Char> ord (Unsafe.Coerce.unsafeCoerce 0)
0
Prelude Data.Char> ord (Unsafe.Coerce.unsafeCoerce 1)
1
Prelude Unsafe.Coerce> 1 + (unsafeCoerce 'A')
66
Prelude Unsafe.Coerce> "hoge" ++ (unsafeCoerce 1)
"hoge"

なんかこの辺汚いなぁとか思わざるを得ませんでした。



そういえば、CleanにはDynamic型がどうこういうのをどこかで見たけど、あれはどうしているんだろう。