haskell – 降低嵌入式语言的功能

如何以尽可能类型安全的方式将
Haskell函数降级为嵌入式语言.特别是,假设我有一个类似的值类型

data Type t where
  Num  :: Type Int
  Bool :: Type Bool

data Ty = TNum | TBool deriving Eq

data Tagged t = Tagged (Type t) t deriving Typeable
data Dynamic  = forall t . Typeable t => Dynamic (Tagged t) deriving Typeable

forget :: Typeable t => Tagged t -> Dynamic
forget = Dynamic

remember :: Typeable b => Dynamic -> Maybe b
remember (Dynamic c) = cast c

我想将像(isSucc :: Int – > Int – > Bool)这样的函数转换为其动态形式和某些类型信息的产品,就像这样

data SplitFun = SF { dynamic    :: [Dynamic] -> Dynamic 
                   , inputTypes :: [Ty] 
                   , outputType :: Ty
                   }

这样对于某些应用功能

(\(a:b:_) -> isSucc a b) == apply (makeDynamicFn isSucc)

模块化一些可能的异常,如果动态类型实际上不匹配则可以抛出.或者,更明确地说,我想找到makeDynamicFn :: FunType – > SplitFun.显然,这不是一个合适的Haskell类型,并且不太可能从isSucc本身中提取类型,所以它可能更像是

anInt . anInt . retBool $isSucc :: SplitFun

其中anInt和retBool具有printf样式类型.

这样的事情可能吗?有没有办法模拟它?

最佳答案 实现FunType类型的函数 – > SplitFun,我们将使用标准类型类机制来解构函数类型.

现在,实现这个功能直接变得相当困难.要从递归情况获取inputTypes和outputType,您必须应用您的函数;但是您只能在动态字段中应用该函数,这使您无法填充其他字段.相反,我们将任务分成两部分:一部分函数将为我们提供Ty信息,另一部分将构建[Dynamic] – >动态功能.

data Proxy a = Proxy

class Split r where
    dynFun :: r -> [Dynamic] -> Dynamic
    tyInfo :: Proxy r -> ([Ty], Ty)

    split :: r -> SplitFun
    split f = let (i, o) = tyInfo (Proxy :: Proxy r)
              in  SF (dynFun f) i o

现在,tyInfo实际上并不需要这个功能,我们只使用Proxy来传递类型信息而无需在所有地方使用undefined.请注意,我们需要ScopedTypeVariables能够从实例声明中引用类型变量r.聪明地使用asTypeOf也可能有效.

我们有两个基本案例:Bool和Int:

instance Split Int where
    dynFun i _ = forget (Tagged Num i)
    tyInfo _ = ([], TNum)

instance Split Bool where
    dynFun b _ = forget (Tagged Bool b)
    tyInfo _ = ([], TBool)

没有输入类型,因为我们已经有了一个值,所以我们不需要请求更多的动态值,只需返回该特定值的Dynamic即可.

接下来,我们有两个递归案例:Bool – > r和Int – > [R

instance (Split r) => Split (Int -> r) where
    dynFun f (d:ds) = case remember d :: Maybe (Tagged Int) of
        Just (Tagged _ i) -> dynFun (f i) ds
        Nothing           -> error "dynFun: wrong dynamic type"
    dynFun f []     = error "dynFun: not enough arguments"

    tyInfo _ = case tyInfo (Proxy :: Proxy r) of
         (i, o) -> (TNum:i, o)

instance (Split r) => Split (Bool -> r) where
    dynFun f (d:ds) = case remember d :: Maybe (Tagged Bool) of
        Just (Tagged _ b) -> dynFun (f b) ds
        Nothing           -> error "dynFun: wrong dynamic type"
    dynFun f []     = error "dynFun: not enough arguments"

    tyInfo _ = case tyInfo (Proxy :: Proxy r) of
         (i, o) -> (TBool:i, o)

这两个需要FlexibleInstances. dynFun检查第一个Dynamic参数,如果没问题,我们可以安全地将函数f应用于它并从那里继续.我们也可以制作dynFun :: r – > [动态] – >也许是动态,但这是相当微不足道的变化.

现在,有一些重复.我们可以介绍另一个类,例如:

class Concrete r where
    getTy   :: Proxy r -> Ty
    getType :: Proxy r -> Type r

然后写:

instance (Typeable r, Concrete r) => Split r where
    dynFun r _ = forget (Tagged (getType (Proxy :: Proxy r)) r)
    tyInfo _ = ([], getTy (Proxy :: Proxy r))

instance (Typeable r, Concrete r, Split s) => Split (r -> s) where
    dynFun f (d:ds) = case remember d :: Maybe (Tagged r) of
        Just (Tagged _ v) -> dynFun (f v) ds
        -- ...

    tyInfo _ = case tyInfo (Proxy :: Proxy s) of
        (i, o) -> (getTy (Proxy :: Proxy r):i, o)

但这需要OverlappingInstances和UndecidableInstances.

点赞