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
也就是说,它的参数必须是同一类型(它们必须匹配)。但是在您的第二次调用中,您将 String
和 Int
传递给它,因此会出现类型错误。
如果您来自像 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
,这是我们的 flip
ped 函数所期望的。 10
是,嗯,是这样的:
> :t 10
Int
在本例中是 Int
。 Int
是 Show
的实例吗?
> show (10 :: Int)
"10"
是的!因此,值 "Ten"
和 10
满足 flip
ped 函数的参数,forall a. Show a => String -> a -> String
,其中 "Ten"
是 String
和 10
, 一个 Int
, 是 Show
.
的实例
现在,让我们看一下失败的案例:
> flip (\n s -> show n <> s) 10 "Ten"
"Ten"
是一个 String
。 String
是 show
的实例吗?
> show "Ten"
"\"Ten\""
是!整洁的!所以没关系。现在,10
是 String
吗?好吧,不,可悲的是,事实并非如此。所以 10
不能用作 flip
ped 函数的第一个参数。
作为 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
也就是说,它的参数必须是同一类型(它们必须匹配)。但是在您的第二次调用中,您将 String
和 Int
传递给它,因此会出现类型错误。
如果您来自像 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
,这是我们的 flip
ped 函数所期望的。 10
是,嗯,是这样的:
> :t 10
Int
在本例中是 Int
。 Int
是 Show
的实例吗?
> show (10 :: Int)
"10"
是的!因此,值 "Ten"
和 10
满足 flip
ped 函数的参数,forall a. Show a => String -> a -> String
,其中 "Ten"
是 String
和 10
, 一个 Int
, 是 Show
.
现在,让我们看一下失败的案例:
> flip (\n s -> show n <> s) 10 "Ten"
"Ten"
是一个 String
。 String
是 show
的实例吗?
> show "Ten"
"\"Ten\""
是!整洁的!所以没关系。现在,10
是 String
吗?好吧,不,可悲的是,事实并非如此。所以 10
不能用作 flip
ped 函数的第一个参数。