为什么这个Haskell代码编译?

ssa*_*anj 17 haskell

鉴于:

uncurry :: (a-> b -> c) -> (a,b) -> c    
id :: a -> a
Run Code Online (Sandbox Code Playgroud)

uncurry id在类型函数中调用结果:(b -> c, b) -> c

我们如何得到这个结果?

如何使用id(a - > a)作为第一个参数来表示,这需要一个(a - > b - > c)函数?

ehi*_*ird 25

如果我们尝试从使类型变得有效的角度来看待它,就会更容易理解:弄清楚我们需要做什么id才能使它适合所需的形状uncurry.既然我们有:

id :: a -> a
Run Code Online (Sandbox Code Playgroud)

我们还有:

id :: (b -> c) -> (b -> c)
Run Code Online (Sandbox Code Playgroud)

这可以通过替换可以看出b -> c对于a原型的id,就像你可以替代Int搞清楚的类型时代替id 42.然后我们可以将括号放在右侧,因为它(->)是右关联的:

id :: (b -> c) -> b -> c
Run Code Online (Sandbox Code Playgroud)

示出id的类型适合的形式a -> b -> c,其中ab -> c.换句话说,我们可以id通过专门化它已经具有的一般类型来重塑类型以适应所需的形式.

理解这一点的另一种方法是看到它uncurry ($)也有类型(b -> c, b) -> c.比较的定义id($):

id :: a -> a
id a = a

($) :: (a -> b) -> a -> b
($) f x = f x
Run Code Online (Sandbox Code Playgroud)

我们可以使后一个定义更加无点:

($) f = f
Run Code Online (Sandbox Code Playgroud)

在这一点上,($)只是id对更具体类型的特化的事实变得清晰.

  • 谢谢!这开始有意义了!我仍然觉得它有点偏差! (2认同)
  • 如果你想变得特别邪恶,你可以用 `(\`id\` 3)` 代替 `($ 3)` 或者将 `id` 定义为 `infixr 0 \`id\`` 并使用它代替`($)`,例如`(^2) \`id\` 1 + 2 * 3`。 (2认同)

Wil*_*ess 8

如何使用id(a - > a)作为第一个参数来表示,这需要一个(a - > b - > c)函数?

实际上,uncurry需要(a -> (b -> c))功能.您看得出来差别吗?:)

省略括号是邪恶的(有时候).这使得新手无法破译Haskell.当然,在你收集了一些语言经验之后,你觉得你根本不再需要它们了.

在这里,一旦我们明确地写出所有省略的括号,一切都变得清晰了:

uncurry :: (a -> (b -> c)) -> ((a,b) -> c)
id      ::  a ->    a
Run Code Online (Sandbox Code Playgroud)

现在,写作uncurry id要求的类型统一a1 -> a1a2 -> (b -> c).这很简单,a1 ~ a2而且a1 ~ (b -> c).只是机械的东西,这里没有创造性思维.所以id问题实际上有类型a -> a where a ~ (b -> c),所以uncurry id有类型(b -> c,b) -> c,通过简单的替换a ~ (b -> c)(a,b) -> c.也就是说,它需要一对b -> c函数和一个b值,并且必须产生一个c值.

由于类型是最一般的(即没有人知道他们,所以没有具体的功能调用,可能做的伎俩在一些特殊的方式),将唯一的办法产生c此值是调用b -> c函数的b值一个论点.当然,这是做什么的($).所以uncurry id == uncurry ($),虽然id肯定不是 ($).