は完全に非慣用的です。私はあなたが望むものなら、あなたは間違った方法で問題を解決しているので、決して使うべきではない答えを提示するつもりです。
A悪いが楽しいソリューション
概要:我々が構築されますボックス - あらゆる型の値。これらのボックスには、関数のアプリケーションと戻り値の型がすべて正しいことを保証するために、等価チェックに使用できる値と型表現が含まれます。次に、関数を適用する前にタイプ表現(コンパイル時に失われた型を表す値)を手動でチェックします。関数と引数の型は不透明であることを覚えておいてください - コンパイル時に消去されているので、sin関数を使う必要がありますunsafeCoerce
。ボックスは、私たちの実存である
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE TypeApplications #-}
import Data.Typeable
import Unsafe.Coerce
:
data Box = forall a. Box (TypeRep, a)
私たちは「安全APIとモジュールを作っていたので、もし私たちが実存種類、typeableと危険な強制が必要で開始する
dスマートなコンストラクタを作成したい:
-- | Convert a type into a "box" of the value and the value's type.
mkBox :: Typeable a => a -> Box
mkBox a = Box (typeOf a, a)
exec
関数今ではこの醜い和のタイプ(Data
)のリストを取る必要はありませんが、ボックスの形でボックスと関数のリストを取ってから、各引数を1つずつ関数に適用して結果。呼び出し側は、戻り値の型(Proxy引数で示される)を静的に知る必要があることに注意してください。そうしないと、結果としてBoxを返さなければならなくなります。
exec :: Typeable a
=> [Box] --^Arguments
-> Box --^Function
-> Proxy a
-> Either String a
exec [] (Box (fTy,f)) p
| fTy == typeRep p = Right $ unsafeCoerce f
-- ^^ The function is fully applied. If it is the type expected
-- by the caller then we can return that value.
| otherwise = Left "Final value does not match proxy type."
exec ((Box (aTy,a)):as) (Box (fTy,f)) p
| Just appliedTy <- funResultTy fTy aTy = exec as (Box (appliedTy, (unsafeCoerce f) (unsafeCoerce a))) p
-- ^^ There is at least one more argument
| otherwise = Left "Some argument was the wrong type. XXX we can thread the arg number through if desired"
-- ^^ The function expected a different argument type _or_ it was fully applied (too many argument supplied!)
私たちは、単に3つの結果をテストすることができます。
降伏
main :: IO()
main =
do print $ exec [mkBox (1::Int), mkBox (2::Int)] (mkBox ((+) :: Int -> Int -> Int)) (Proxy @Int)
print $ exec [mkBox (1::Int)] (mkBox (last :: [Int] -> Int)) (Proxy @Int)
print $ exec [mkBox (1::Int)] (mkBox (id :: Int -> Int)) (Proxy @Double)
:
Right 3
Left "Some argument was the wrong type. XXX we can thread the arg number through if desired"
Left "Final value does not match proxy type."
EDITを:私はBox
と、このAPIは、より多くの教育やので、必要以上に少ない簡潔であることを言及する必要がありますがData.Dynamic
を使用できます。たとえば、(プロキシーが推測できるのでAPIも変更しました):
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE GADTs #-}
import Data.Dynamic
import Type.Reflection
type Box = Dynamic
-- | Convert a type into a "box" of the value and the
-- value's type.
mkBox :: Typeable a => a -> Box
mkBox = toDyn
exec :: Typeable a
=> [Box] --^Arguments
-> Box --^Function
-> Either String a
exec [] f = case fromDynamic f of
Just x -> Right x
Nothing -> Left "Final type did not match proxy"
exec (a:as) f
| Just applied <- dynApply f a = exec as applied
| otherwise = Left "Some argument was the wrong type. XXX we can thread the arg number through if desired"
main :: IO()
main =
do print (exec [mkBox (1::Int), mkBox (2::Int)] (mkBox ((+) :: Int -> Int -> Int)) :: Either String Int)
print (exec [mkBox (1::Int)] (mkBox (last :: [Int] -> Int)) :: Either String Int)
print (exec [mkBox (1::Int)] (mkBox (id :: Int -> Int)) :: Either String Double)
あなたは 'Data.Dynamic'に精通していますか? –