我認爲你所要求的是完全不地道。我將提出一個你永遠不應該使用的答案,因爲如果它是你想要的,那麼你正在以錯誤的方式解決問題。
一個壞的,但有趣的解決方案
概述:我們將構建盒 - 的任何類型的值。這些框將攜帶我們可用於相等性檢查的值和類型表示,以確保我們的函數應用程序和返回類型都是正確的。然後,我們在應用函數之前手動檢查類型表示(表示類型的值,這些值在編譯時丟失)。記住函數和參數類型是不透明的 - 它們在編譯時被擦除 - 所以我們需要使用有罪功能unsafeCoerce
。
所以先從我們需要生存類型,分型和不安全的要挾:
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE TypeApplications #-}
import Data.Typeable
import Unsafe.Coerce
這個盒子是我們的生存:
data Box = forall a. Box (TypeRep, a)
如果我們做一個模塊提供一個安全的API,我們」 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
),而是可以以框的形式取出框和函數的列表,然後將每個參數應用於函數,以獲得結果。注意,調用者需要靜態地知道返回類型 - 由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!)
我們可以測試三種結果只是:
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."
編輯:我應該指出,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'? –