Haskell中函数定义中括号的重要性与参数的数据类型有关。
例如:
doStuff Name -> Age -> String
doStuff (NameConstr a) (AgeConstr b) = "Nom: " ++ a ++ ", age: " ++ b
预先在某处定义了以下内容:
data Name = NameConstr String
data Age = AgeConstr Integer
能否以一种无需括号的方式捕获函数参数a和b?
仅供参考,我正在努力:
我似乎无法掌握这个更精细的细节。
答案 0 :(得分:7)
如果没有括号,该函数将被视为具有四个参数。我想不出一个反例,即省略括号会导致含糊不清。
如果需要,您可以按如下方式重新定义类型:
data Name = NameConstr { getName :: String }
data Age = AgeConstr { getAge :: Integer }
这样你的功能就可以成为:
doStuff n a = "Nom: " ++ getName n ++ ", age: " ++ show (getAge a)
(修复了最后一部分; a
是Integer
,不能连接成字符串)
答案 1 :(得分:4)
实际上,可以解析(甚至嵌套的)模式的简单语法而根本没有parens。假设这样一个:
<PAT> ::= <WILDCARD> | <VAR> | <CON0> | <CON1> <PAT> | <CON2> <PAT> <PAT> ...
<VAR> ::= <LNAME>
<CON*> ::= <UNAME>
<WILD> ::= "_"
其中LNAME是以小写字母开头的名称,UNAME以大写字母开头。解析时我们应该查找构造函数名称,以便我们找到它的arity。然后我们可以使用arity信息解析构造函数字段。但是这种查找可能会使解析本身变得复杂化并减慢其速度。 Haskell有更复杂的模式(视图模式,“作为”模式,记录,具有任意固定性的中缀构造函数,e.t.c。)和省略parens可能导致歧义。
虽然还有其他原因不这样做。请考虑以下代码:
data Bar = Bar Int
data Foo = Foo Int
libFunction Foo a Bar b = a + b
someUse bar foo = libFunction foo bar
接下来想象我们稍微更改一下数据类型:
data Bar = Bar
data Foo = Foo Int Bar Int
修改后的代码可能仍然存在类型错误,但该函数不符合我们的预期。虽然不是现实世界的例子。而且由于Haskell有类型类,因此很难找出出错的地方。
换句话说:我们可以放松错误消息的质量,并且parens会在变更后保护我们免受意外行为的影响。
答案 2 :(得分:1)
这有点傻,但在这种情况下实际上有一种避免括号的方法:
doStuff :: Name -> Age -> String
NameConstr a `doStuff` AgeConstr b = "Nom: " ++ a ++ ", age: " ++ b
这与定义中缀运算符的方式完全相同,并且使用反引号来表示非运算符标识符在将其定义为应用时也同样适用。
我不建议使用不希望在backtick-y中缀样式中使用的函数实际执行此操作。