简体   繁体   中英

Haskell data type function parameter

What is the significance of the parenthesis in a function definition in Haskell with respect to the data type of parameters.

For example:

doStuff Name -> Age -> String
doStuff (NameConstr a) (AgeConstr b) = "Nom: " ++ a ++ ", age: " ++ b

with the following defined somewhere beforehand:

data Name = NameConstr String
data Age = AgeConstr Integer

Could the function parameters a and b be captured in a way that negates the need for parenthesis here?

FYI, I'm working through:

and I just can't seem grasp this finer detail yet.

Without parentheses, the function would be deemed to have four parameters. I can't think of a counterexample where omitting brackets would lead to ambiguity, though.

If you want, you can redefine your types as follows:

data Name = NameConstr { getName :: String  }
data Age  = AgeConstr  { getAge  :: Integer }

so that your function can become:

doStuff n a = "Nom: " ++ getName n ++ ", age: " ++ show (getAge a)

(fixed the last part; a is an Integer and cannot be concatenated to a string)

Indeed, it's possible to parse simple grammar for (even nested) patterns without parens at all. Suppose such one:

<PAT>  ::= <WILDCARD> | <VAR> | <CON0> | <CON1> <PAT> | <CON2> <PAT> <PAT> ...
<VAR>  ::= <LNAME>
<CON*> ::= <UNAME>
<WILD> ::= "_"

where LNAME is names that starts with lowercase letter and UNAME starts with uppercase letter. While parsing we should look up constructor name so we can find out its arity. Then we can parse constructor fields using arity information. But this lookup might significant complicate and slow down parsing itself. Haskell has much more complex patterns(view patterns, "as" patterns, records, infix constructors with arbitrary fixity, etc) and omitting parens can lead to ambiguity.

Though there is another reason not to do that. Consider the following code:

data Bar = Bar Int
data Foo = Foo Int

libFunction Foo a Bar b = a + b
someUse bar foo = libFunction foo bar

Next imagine we change datatypes a bit:

data Bar = Bar
data Foo = Foo Int Bar Int

Modified code might still typecheck, but the function will do not that we expect. Not a real world example but nevertheless. And since Haskell have type classes it can be pretty hard to find out where we get something wrong.

In other words: we can loose quality of error messages and parens defends us from unexpected behaviour after changes.

It's slightly silly, but in this case there is actually a way to avoid parentheses:

doStuff :: Name -> Age -> String
NameConstr a `doStuff` AgeConstr b = "Nom: " ++ a ++ ", age: " ++ b

This is exactly the same way you define an infix operator, and using backticks to infix-ify a non-operator identifier works just as well when defining it as when applying it.

I don't recommend actually doing this with functions you don't expect to be used in backtick-y infix style, though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM