简体   繁体   中英

case-of / case expression with or in pattern matching possible?

I am learning haskell on my own. And was working on implementing a custom List data type using basic lists and case of.

So data structure is something similar to this

data List = List [String] | EmptyList deriving Show

now if I am doing case expressions for base case, I have to do two matchings. A simple example would be the size function

size :: List -> Int
size lst = case lst of
  (List []) -> 0
  EmptyList -> 0
  (List (x:xs)) -> 1 + size (List xs)

Can't I do something like combining the two base cases of list being empty (List []) and EmptyList somehow to reduce redundancy?

size :: List -> Int
size lst = case lst of
  (List []) | EmptyList -> 0
  (List (x:xs)) -> 1 + size (List xs)

I have tried searching all over the net for this, but unfortunately wasn't able to find anything concrete over matching multiple patterns in one case.

First of all you should consider why you have separate constructors for List and EmptyList in the first place. The empty list clearly is already a special case of a list anyway, so this is an awkward redundancy. If anything, you should make it

import Data.List.NonEmpty
data List' a = NEList (NonEmpty a) | EmptyList

Another option that would work for this specific example is to make the empty case into a “catch-all pattern”:

size :: List -> Int
size lst = case lst of
  (List (x:xs)) -> 1 + size (List xs)
  _ -> 0

BTW there's no reason to use case here, you can also just write two function clauses:

size :: List -> Int
size (List (x:xs)) = 1 + size (List xs)
size _ = 0

Anyways – this is generally discouraged, because catch-all clauses are an easy place for hard to detect bugs to creep in if you extend your data type in the future.

Also possible, but even worse style is to use a boolean guard match – this can easily use lookups in a list of options, like

size lst | lst`elem`[EmptyList, List []] = 0
size (List (x:xs)) = 1 + size (List xs)

Equality checks should be avoided if possible; they introduce an Eq constraint which, quite needlessly, will require the elements to be equality-comparable. And often equality check is also more computationally expensive than a pattern match.

Another option if you can't change the data structure itself but would like to work with it as if List [] and EmptyList were the same thing would be to write custom pattern synonyms . This is a relatively recent feature of Haskell; it kind of pretends the data structure is actually different – like List' – from how it's really layed out.

In the comments, you say

there are no such functions [which should return different results for EmptyList and List [] ]

therefore I recommend merging these two constructors in the type itself:

data List = List [String] deriving Show

Now you no longer need to distinguish between EmptyList and List [] in functions that consume a List .

...in point of fact, I would go even further and elide the definition entirely, simply using [String] everywhere instead. There is one exception to this: if you need to define an instance for a class that differs in behavior from [String] 's existing instance. In that exceptional case, defining a new type is sensible; but I would use newtype instead of data , for the usual efficiency and semantics reasons.

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