operator <> 在 Purescript 中究竟是如何工作的?

How exactly does operator <> work in Purescript?

作为 Purescript 的新手,我正在努力弄清楚为什么以下代码(摘自 "PureScript By Example")会如此工作:

> flip (\n s -> show n <> s) "Ten" 10
"10Ten"

这对我来说很有意义:flip 以相反的顺序调用它的第一个参数(lambda 表达式)和它的第二个和第三个参数,从而生成连接的字符串。

但我想知道为什么我们使用此代码段会得到以下响应:

> flip (\n s -> show n <> s) 10 "Ten"

Could not match type Int with type String

这是我的思路:运算符 <> 实际上是 Data.Semigroup.append 的 shorthand 被调用 n 推导出 String (一个Data.Semigroup) 和 s 的实例推断为 Int。那么为什么 <> 不能将 Int 附加到 String 呢? (我想是因为它是右结合的,但我不确定......)

要明确...

flip (\n s -> show n <> s) "Ten" 10  == show 10 <> "Ten"
flip (\n s -> show n <> s) 10 "Ten"  == show "Ten" <> 10

(<>)Data.Semigroup.append 的别名)的类型为:

append :: a -> a -> a

也就是说,它的参数必须是同一类型(它们必须匹配)。但是在您的第二次调用中,您将 StringInt 传递给它,因此会出现类型错误。

如果您来自像 javascript 这样具有隐式类型强制的弱类型语言,这种行为可能会令人惊讶。

所以让我们把它分开一点。一、原函数:

flip (\n s -> show n <> s) "Ten" 10

如果我们获取每个部分的类型,我们会看到什么?

> :t flip
forall a b c. (a -> b -> c) -> b -> a -> c

这当然只是flip,它接受一个双参数函数并将其转换为另一个参数翻转的函数。

接下来,这是有趣的部分,flip(a -> b -> c),变成 b -> a -> c:

> :t (\n s -> show n <> s)
forall t4. Show t4 => t4 -> String -> String

好的,那么,它是如何得出 t4 -> String -> String 的?此函数中唯一引用 String 的函数是 show:

> :t show
forall a. Show a => a -> String

此外,这就是 t4 上的 Show 约束的来源。 PureScript 告诉我们的是 (\n s -> show n <> s) 是一个函数,它有两个参数,return 是一个 String

它正在调用我们的第一个参数 t4,它是此会话的有效且唯一的类型变量。它也不能告诉我们关于 t4 的任何信息,除了因为 show 需要 Show 的实例,t4 必须是 Show 的实例。

现在,在我们的 Show => t4 上调用 show 将 return 一个 String,我们希望 show 这样做:

> :t show
forall a. Show a => a -> String

(是的,我们已经看到了。)因此,在我们的函数 (\n s -> show n <> s) 中,show n 项的类型为 String。这是因为 show 已完全应用 t4,它是 Show 的实例,因此编译器可以推断 show n 将具有 [=28] 的类型=], String.

现在有趣的地方来了。 <> 最一般的形式只有一个类型参数:

> :t (<>)
forall a. Semigroup a => a -> a -> a

它是一个函数,它有两个 a 类型的值,return 是一个 a 类型的新值。请注意,虽然函数有一个类型参数,但它只有一个类型参数,因此任何特定的 (<>) 在其类型上都不会是多态的。

现在,虽然我们的函数 在其第一个参数中是多态的 ,但它也只有一个类型变量。无论如何,这实际上有点转移注意力,因为让我们看看 flip 做了什么:

> :t flip (\n s -> show n <> s)
forall t5. Show t5 => String -> t5 -> String

哇哦。我们的多态参数仍然存在,但现在是第二个。这就是 flip 所做的。那么这到底是什么意思呢?

> flip (\n s -> show n <> s) "Ten" 10

在这种情况下,"Ten"String,这是我们的 flipped 函数所期望的。 10 是,嗯,是这样的:

> :t 10
Int

在本例中是 IntIntShow 的实例吗?

> show (10 :: Int)
"10"

是的!因此,值 "Ten"10 满足 flipped 函数的参数,forall a. Show a => String -> a -> String,其中 "Ten"String10, 一个 Int, 是 Show.

的实例

现在,让我们看一下失败的案例:

> flip (\n s -> show n <> s) 10 "Ten"

"Ten" 是一个 StringStringshow 的实例吗?

> show "Ten"
"\"Ten\""

是!整洁的!所以没关系。现在,10String 吗?好吧,不,可悲的是,事实并非如此。所以 10 不能用作 flipped 函数的第一个参数。