给出的是像
这样的Javascript函数const isNull = x => x === null ? [] : [isNull];
这样的功能可能是无稽之谈,但这不是问题。
当我尝试表达类似Haskell的类型注释时,我失败了。同样尝试在Haskell中实现类似的功能:
let isZero = \n -> if n == 0 then [] else [isZero] -- doesn't compile
这种函数是否有一个术语本身不递归,但在它们的类型中是递归的?这些函数只能用动态类型语言表达吗?
很抱歉,如果这很明显 - 我的Haskell知识(包括严格的类型系统)是相当肤浅的。
答案 0 :(得分:5)
您需要为此定义一个显式递归类型。
newtype T = T (Int -> [T])
isZero :: T
isZero = T (\n -> if n == 0 then [] else [isZero])
支付的价格是T
构造函数的包装/展开,但这是可行的。
如果你想模仿类似Javascript的无类型世界(AKA统一或动态输入),你甚至可以使用
data Value
= VInt Int
| VList [Value]
| VFun (Value -> Value)
...
(谨防known bug)
原则上,每个Javascript值都可以用上面的巨大和类型表示。例如,应用程序就像
jsApply (VFun f) v = f v
jsApply _ _ = error "Can not apply a non-function value"
注意静态类型检查如何以这种方式转换为动态检查。同样,静态类型错误也会变成运行时错误。
答案 1 :(得分:3)
Chi展示了如何实现这种无限类型:你需要一个newtype包装器来“隐藏”无限递归。
一个有趣的选择是使用fixpoint formulation。回想一下,你可以伪造一些像你的例子一样递归的东西
isZero = fix $ \f n -> if n == 0 then [] else [f]
同样,类型实际上可以表示为相关仿函数的fixpoint,即(Int ->)
和[]
的组合(在变换器中)格式塔是ListT
):
isZero :: Fix (ListT ((->) Int))
isZero = Fix . ListT $ \n -> if n==0 then [] else [isZero]
另外值得注意的是,您可能并不真正想要ListT
。如果您只有零个或一个元素,那么MaybeT
会更自然。更妙的是,您可以使用仿函数修复点为closely related to the free monad这一事实,它可以为您提供“possibly trivial” alternative case:
isZero' :: Free ((->) Int) ()
isZero' = wrap $ \n -> if n==0 then Pure () else isZero'
monad实例中的 Pure ()
仅为return ()
,因此您也可以使用标准when
替换if
构造:
isZero' = wrap $ \n -> when (n/=0) isZero'