简体   繁体   中英

What's the best style in Haskell

Made serious progress also thanks to Erik Meijer's lectures. Good watch, maybe a hint. Haskell allows for several ways to write the same function. Which one of these would be best in terms of efficiency and readability?

sqr' = \x -> x * x
sqr'' x = x * x
sqr''' = (^2)

Between these two top-level definitions:

sqr' = \x -> x * x
sqr'' x = x * x

the second is pretty much universally preferred in Haskell programs. Search through almost any chunk of real-world Haskell code, and you will find many examples of the second but few of the first. Instead, "lambda abstraction" (ie, the \\x -> ... syntax) is most often used for defining anonymous functions to pass as arguments to higher order functions.

There are a couple of reasons the second syntax is preferred. First, it's literally more concise and, from a readability perspective, incorporates fewer distinct syntactic elements (ie, juxtaposition and the = operator, instead of juxtaposition, = , \\ , and -> ). It also generalizes well to the common Haskell idiom of defining a function using multiple patterns:

factorial 0 = 1
factorial n | n > 0 = n * factorial (n-1)

To do this with the lambda syntax, you'd need to add an explicit case construct, involving yet another set of syntactic elements.

Between:

sqr'' x = x * x
sqr''' = (^2)

or -- perhaps a fairer comparison -- between:

sqr'''' x = x^2
sqr''' = (^2)

it's more a matter of personal preference. Many Haskell programmers like the clean look of so-called point-free syntax, where larger functions are made up using higher-order functions and/or chains of composed functions without explicit arguments, like:

mostFrequentWord
   = head . maximumBy (comparing length) . group . sort . words

and definitions like sqr''' are more in line with this overall style.

In terms of differences in meaning between these forms, it's actually a little complicated. For obscure reasons having to do with things called "the monomorphism restriction" and "defaulting rules", if you took the following module:

module Square where
sqr' = \x -> x * x
sqr'' x = x * x
sqr''' = (^2)

and compiled it with ghc -O , the definitions of sqr' and sqr''' would be equivalent -- both would be specialized to operate on the Integer type and would generate exactly the same code. (Tested with GHC 8.0.2). In contrast, sqr'' remains polymorphic with signature Num a => a -> a , meaning it can operate on any numeric type.

If you add top-level type signatures (good practice anyway!), like so:

module Square where
sqr', sqr'', sqr''' :: (Num a) => a -> a
sqr' = \x -> x * x
sqr'' x = x * x
sqr''' = (^2)

then they all generate exactly the same code. You can verify this yourself by peeking at the generated "core" (the intermediate Haskell-like language that the compiler creates as a midpoint in the compilation process) using:

ghc -O -ddump-simpl -dsuppress-all -fforce-recomp Square.hs

In the generated core, you'll see the definition:

sqr' = \ @ a_aBC $dNum_aLW x_arx -> * $dNum_aLW x_arx x_arx

which looks weird, but basically says, apply the * operation for the appropriate Num type to the arguments x_arx x_arx . The generated code for the two variants:

sqr'' = sqr'
sqr''' = sqr'

shows that GHC sees no difference between them and sqr' , and so there will be no semantic or performance difference.

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