如何解决F#的类型系统

时间:2013-12-29 08:09:58

标签: haskell f#

在Haskell中,您可以使用unsafeCoerce覆盖类型系统。如何在F#中做同样的事情?

例如,实现Y-combinator。

2 个答案:

答案 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中提到的各种编码技巧。