繁体   English   中英

如何解决F#的类型系统

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM