我想将Haskell函数提升为更高阶的lambda演算编码。这几乎是从Oleg的Typed Tagless Final编码中逐字记录的。
class Lam r where
emb :: a -> r a
(^) :: r (r a -> r a) -> (r a -> r a)
lam :: (r a -> r a) -> r (r a -> r a)
instance Lam Identity where
emb = Identity
f ^ x = f >>= ($ x)
lam f = return (f . return =<<) -- call-by-value
eval = runIdentity
我可以使用Lam
将任意Haskell类型嵌入emb
,但我不能将(^)
用于应用程序。此外,提升的功能会表现得很懒散。相反,我必须通过应用程序解除它们的应用程序。
emb1 :: ( Applicative r, Lam r )
=> (a -> b) -> r (r a -> r b)
emb1 f = lam $ \ra -> f <$> ra
emb2 :: ( Applicative r, Lam r )
=> (a -> b -> c) -> r (r a -> r (r b -> r c))
emb2 f = lam $ \ra -> lam $ \rb -> f <$> ra <*> rb
emb3 :: ( Applicative r, Lam r )
=> (a -> b -> c -> d)
-> r (r a -> r (r b -> r (r c -> r d)))
emb3 f = lam $ \ra -> lam $ \rb -> lam $ \rc -> f <$> ra <*> rb <*> rc
>>> eval $ emb2 (+) ^ emb 1 ^ emb 2
3
但是,这是很多样板。我想创建一个适用于任何arity函数的通用提升函数。我觉得有可能使用类似于Printf
的{{1}}或PrintfType
fixed-vector
类型的内容。我可以使用类型函数指定我想要的东西
Cont
但是我通过这种方法非常困难,通常是由于注意力问题。我能够通过新类型包装器和范围类型变量的真正可怕组合来解决注入性,但它实际上从未进行过类型检查。
这可以在Haskell中表达吗?
答案 0 :(得分:3)
你可能想看一下
无标签最终样式中的Ordinary and one-pass CPS transformation。诀窍是在对象语言中概括箭头类型。我们经常使用Haskell的事实
对象语言(要嵌入)中的函数类型的类型构造函数->
是巧合和方便。通常,对象函数不会简单地映射到Haskell函数。所引用文章中的代码包含ESymantics
-- How to interpret arrows and other types
type family Arr (repr :: * -> *) (a :: *) (b :: *) :: *
class ESymantics repr where
int :: Int -> repr Int
add :: repr Int -> repr Int -> repr Int
lam :: (repr a -> repr b) -> repr (Arr repr a b)
app :: repr (Arr repr a b) -> repr a -> repr b
现在我们有足够的自由来解释Arr,具体取决于特定的repr。所引用的文章解释了CPS实例的Arr。
编辑:事实上,我们可以达到同样的效果 - 重新定义对象语言的箭头含义 - 不引入Arr类型(具有注入问题)和没有ESemantics。普通和单程CPS转换的上述链接显示了新代码,使用标准语义并重新解释函数类型构造函数的含义。不再存在任何注入问题。