我在 Haskell 中有一些不同类型的函数。
我需要将它们保存在某个异构列表中,因此请使用 Data.Dynamic 之类的东西。
我想从此列表中获取函数并将它们组合在一起以获得不同类型的新函数。
例如我有函数
addDoubles :: Double -> Double -> Double
和 fromInt :: Int -> Double
那么我可以编写一个像 composedFn = (. fromInt) . addDoubles . fromInt
这样的函数,然后应该将其保存为类型为 Int -> Int -> Double
的动态
我很难让 ghc 相信这些类型都是正确的。
非常感谢任何帮助:)
就像我说的,我已经尝试过 Data.Dynamic ...
{-# LANGUAGE GADTs #-}
import Data.Dynamic
import Data.Data
fromInt :: Int -> Double
fromInt = fromIntegral
addDubs :: Double -> Double -> Double
addDubs = (+)
-- I want to store many fns in some collection all with different types
myfuncs :: [Dynamic]
myfuncs = [toDyn fromInt, toDyn addDubs]
-- I want to be able to combine these functions to get new types
partialApplyFns2Fn :: (a -> b) -> (c -> d) -> (d -> b -> e) -> (c -> a -> e)
partialApplyFns2Fn r l f = (. r) . f . l
combineHard :: Dynamic -> Dynamic -> Dynamic -> Maybe Dynamic
combineHard d1 d2 d3 = do
f1 <- fromDynamic d1 :: Maybe (Int -> Double)
f2 <- fromDynamic d2 :: Maybe (Int -> Double)
f3 <- fromDynamic d3 :: Maybe (Double -> Double -> Double)
return $ toDyn ((. f1) . f3 . f2)
这可行,但是,我不想对这些类型进行硬编码。在代码的其他地方,我会知道这些类型以及组合 Fn 的 TypeRep,所以理想情况下会做这样的事情......
combineDynams :: (TypeRep, Dynamic) -> (TypeRep, Dynamic) -> (TypeRep, Dynamic) -> TypeRep -> Dynamic
combineDynams (tr1, f1) (tr2, f2) (tr3, f3) targetTypeRep = do
f1' <- fromDynamic f1 :: Maybe tr1
f2' <- fromDynamic f2 :: Maybe tr2
f3' <- fromDynamic f3 :: Maybe tr3
return $ toDyn (partialApplyFns2Fn f1' f2' f3')
但这显然行不通。
我也尝试过使用演员表进行 GADT,但遇到了类似的问题。
data AnyFunc where
AnyUnary :: (Typeable a, Typeable b) => (a -> b) -> AnyFunc
AnyBinary :: (Typeable c, Typeable d, Typeable e) => (c -> d -> e) -> AnyFunc
-- Function that checks type compatibility at runtime
anyFuncMaker :: AnyFunc -> AnyFunc -> AnyFunc -> Maybe AnyFunc
anyFuncMaker (AnyUnary l) (AnyUnary r) (AnyBinary f) =
case cast f of
Just f' -> Just $ AnyBinary (partialApplyFns2Fn l r f')
Nothing -> Nothing -- Type mismatch
对于基于 GADT 的解决方案,我收到错误:
• Could not deduce ‘Typeable e0’ arising from a use of ‘cast’
from the context: (Typeable a, Typeable b)
似乎
cast
需要更多关于双参数函数的最终结果类型的指导。 GHC 需要确保当我们转换双参数函数时,结果类型与原始函数的类型相同。
我们需要“绑定”该类型,以便引用它并说服 GHC。我们可以用这个笨拙的辅助函数来做到这一点:
anyFuncMaker' :: AnyFunc -> AnyFunc -> AnyFunc -> Maybe AnyFunc
anyFuncMaker' (AnyUnary l) (AnyUnary r) (AnyBinary f) = AnyBinary <$> annoyingAux l r f
where
annoyingAux
:: forall a b c d d' b' e .
(Typeable a, Typeable b, Typeable c, Typeable d, Typeable d', Typeable b', Typeable e)
=> (a -> b) -> (c -> d) -> (d' -> b' -> e) -> Maybe (c -> a -> e)
annoyingAux l' r' g =
case cast g of
Just g' -> Just $ (partialApplyFns2Fn l' r' g')
Nothing -> Nothing
anyFuncMaker' _ _ _ = Nothing
也许有一种更简单的方法来定义辅助函数,但我不确定。我们可以使用模式中的类型抽象(GHC 9.2 及以上版本)来简化定义,而不是辅助函数:
anyFuncMaker'' :: AnyFunc -> AnyFunc -> AnyFunc -> Maybe AnyFunc
anyFuncMaker'' (AnyUnary @_ @b l) (AnyUnary @_ @d r) (AnyBinary @_ @_ @e f) =
case cast f of
Just (f' :: d -> b -> e) -> Just $ AnyBinary (partialApplyFns2Fn l r f')
Nothing -> Nothing
anyFuncMaker'' _ _ _ = Nothing