作为Purescript的新手,我正在努力弄清楚为什么以下代码(取自“PureScript By Example”)的工作原理如下:
> flip (\n s -> show n <> s) "Ten" 10
"10Ten"
这对我来说很有意义:以相反的顺序翻转第一个参数(lambda表达式)及其第二个和第三个参数,从而产生连接的字符串。
但我想知道为什么我们会用这个片段得到以下回复:
> flip (\n s -> show n <> s) 10 "Ten"
Could not match type Int with type String
以下是我的思路:运算符<>
实际上是Data.Semigroup.append
的简写,调用时n
推导为String
(Data.Semigroup
的实例})和s
推导为Int
。那么为什么<>
无法将Int
附加到String
? (我想因为它是正确联想的,但我不确定......)
答案 0 :(得分:2)
要清楚......
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),则此行为可能会令人惊讶。
答案 1 :(得分:1)
所以让我们把它分开一点。一,原始功能:
flip (\n s -> show n <> s) "Ten" 10
如果我们采用每个部分的类型,我们会看到什么?
> :t flip
forall a b c. (a -> b -> c) -> b -> a -> c
当然这实际上只是flip
,它需要一个双参数函数并将其转换为另一个参数翻转。
接下来,这是有趣的部分,(a -> b -> c)
的{{1}},它变为flip
:
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
上的Show
约束来自的地方。 PureScript告诉我们的是t4
是一个函数,它需要两个参数,并返回(\n s -> show n <> s)
。
它正在调用我们的第一个参数String
,它是此会话的有效且唯一的类型变量。它也无法告诉我们t4
的任何内容,但由于t4
需要show
的实例,Show
必须是t4
的实例。
现在,在我们的Show
上调用show
将返回Show => t4
,我们希望String
能够这样做:
show
(是的,我们已经看过了。)因此,在我们的函数> :t show
forall a. Show a => a -> String
中,(\n s -> show n <> s)
字词的类型为show n
。这是因为String
已完全应用show
,这是t4
的一个实例,因此编译器可以推断出Show
的类型为{{1} },show n
。
现在这是它变得有趣的地方。 show
最常见的形式有一个类型参数:
String
这是一个函数,它需要两个<>
类型的值,并返回一个类型为> :t (<>)
forall a. Semigroup a => a -> a -> a
的新值。请注意,虽然函数具有类型参数,但它只有一个类型参数,因此任何特定的a
在其类型中都不会是多态的。
现在,虽然我们的函数在其第一个参数中是多态的,但它也只有一个类型变量。这实际上有点像红鲱鱼,因为让我们看看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"
ped函数需要它。 String
就是这样:
flip
在这种情况下,它是10
。 > :t 10
Int
是Int
的实例吗?
Int
是的!因此,值Show
和> show (10 :: Int)
"10"
满足"Ten"
ped函数10
的参数,其中flip
为forall a. Show a => String -> a -> String
和{{1 {},"Ten"
是String
的实例。
现在,让我们看一下失败的案例:
10
Int
是Show
。 > flip (\n s -> show n <> s) 10 "Ten"
是"Ten"
的实例吗?
String
是的!整齐!所以没关系。现在,String
是show
吗?嗯,不,可悲的是,事实并非如此。因此> show "Ten"
"\"Ten\""
不能用作10
ped函数的第一个参数。