[英]How to work around F#'s type system
在Haskell中,您可以使用unsafeCoerce
来覆盖类型系统。 如何在F#中做同样的事情?
例如,实现Y-combinator。
我想提供一个不同的解决方案,基于将无类型lambda演算嵌入到类型化的函数语言中。 我们的想法是创建一种数据类型,允许我们在类型α和α→α之间进行切换,从而允许我们逃避类型系统的限制。 我对F#不太熟悉所以我会在Haskell中给出答案,但我相信它可以很容易地调整(也许唯一的复杂因素可能是F#的严格性)。
-- | Roughly represents morphism between @a@ and @a -> a@.
-- Therefore we can embed a arbitrary closed λ-term into @Any a@. Any time we
-- need to create a λ-abstraction, we just nest into one @Any@ constructor.
--
-- The type parameter allows us to embed ordinary values into the type and
-- retrieve results of computations.
data Any a = Any (Any a -> a)
请注意,类型参数对于组合术语并不重要。 它只允许我们将值嵌入到表示中并在以后提取它们。 特定类型的所有术语Any a
都可以自由组合而不受任何限制。
-- | Embed a value into a λ-term. If viewed as a function, it ignores its
-- input and produces the value.
embed :: a -> Any a
embed = Any . const
-- | Extract a value from a λ-term, assuming it's a valid value (otherwise it'd
-- loop forever).
extract :: Any a -> a
extract x@(Any x') = x' x
使用此数据类型,我们可以使用它来表示任意无类型的lambda术语。 如果我们想将Any a
的值解释为函数,我们只需打开它的构造函数。
首先让我们定义函数应用程序:
-- | Applies a term to another term.
($$) :: Any a -> Any a -> Any a
(Any x) $$ y = embed $ x y
和λ抽象:
-- | Represents a lambda abstraction
l :: (Any a -> Any a) -> Any a
l x = Any $ extract . x
现在我们拥有创建复杂λ项所需的一切。 我们的定义模仿经典的λ项语法,我们所做的只是使用l
来构造λ抽象。
让我们定义Y组合子:
-- λf.(λx.f(xx))(λx.f(xx))
y :: Any a
y = l (\f -> let t = l (\x -> f $$ (x $$ x))
in t $$ t)
我们可以用它来实现Haskell的经典fix
。 首先,我们需要能够将a -> a
的函数嵌入到Any a
:
embed2 :: (a -> a) -> Any a
embed2 f = Any (f . extract)
现在很容易定义
fix :: (a -> a) -> a
fix f = extract (y $$ embed2 f)
然后是递归定义的函数:
fact :: Int -> Int
fact = fix f
where
f _ 0 = 1
f r n = n * r (n - 1)
请注意,在上面的文本中没有递归函数。 唯一的递归是在Any
数据类型中,它允许我们定义y
(也是非递归定义的)。
在Haskell中, unsafeCoerce
具有类型a -> b
,并且通常用于向编译器声明被强制的东西实际上具有目标类型,并且只是类型检查器不知道它。
另一种不太常见的用法是将位模式重新解释为另一种类型。 例如,未装箱的Double#
可以重新解释为未装箱的Int64#
。 您必须确保这是安全的基础表示。
在F#中,第一个应用程序可以使用box |> unbox
实现,正如John Palmer在对该问题的评论中所说的那样。 如果可能的话,使用显式类型参数来确保您不会意外地推断出强制的强制,例如box<'a> |> unbox<'b>
其中'a
和'b
是类型变量或已经存在的具体类型代码中的范围。
对于第二个应用程序,请查看BitConverter类以获取位模式的特定转换。 从理论上讲,你也可以做一些类似于与非托管代码的接口来实现这一目标,但这似乎非常重要。
这些技术不适用于实现Y组合子,因为强制转换仅在运行时对象实际具有目标类型时才有效,但是对于Y组合器,您实际上需要再次调用相同的函数但具有不同的类型。 为此,您需要John Palmer链接到的问题中提到的各种编码技巧。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.