在Haskell中,您可以使用unsafeCoerce
覆盖类型系统。如何在F#中做同样的事情?
例如,实现Y-combinator。
答案 0 :(得分:5)
我想提供一种不同的解决方案,基于在类型化的函数语言中嵌入无类型的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)
请注意,type参数对于组合术语并不重要。它只允许我们将值嵌入到表示中并在以后提取它们。特定类型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
(也是非递归定义的)。
答案 1 :(得分:2)
在Haskell中,unsafeCoerce
的类型为a -> b
,通常用于向编译器声明被强制的东西实际上具有目标类型,而它只是类型检查器我不知道。
另一种不太常见的用法是将位模式重新解释为另一种类型。例如,未装箱的Double#
可以重新解释为未装箱的Int64#
。为了安全起见,你必须确定基础表示。
在F#中,第一个应用程序可以通过box |> unbox
实现,正如John Palmer在对该问题的评论中所说的那样。如果可能的话,使用明确的类型参数来确保您不会意外地推断出强制性的强制,例如: box<'a> |> unbox<'b>
其中'a
和'b
是类型变量或已在代码范围内的具体类型。
对于第二个应用程序,请查看BitConverter class以获取位模式的特定转换。从理论上讲,你也可以做一些类似于与非托管代码的接口来实现这一目标,但这似乎非常重要。
这些技术不适用于实现Y组合器,因为只有在运行时对象确实具有目标类型时,强制转换才有效,但是对于Y组合器,您实际上需要再次调用相同的函数但是不同的类型。为此,您需要the question John Palmer linked to中提到的各种编码技巧。