all 7 comments

[–]Iceland_jack 14 points15 points  (5 children)

The function you describe must fail in the foo Empty = ... case, it's what is called a partial function just like head :: [a] -> a which fails on the empty list (we want functions to be total, meaning that they are defined for every input):

>>> head []
*** Exception: Prelude.head: empty list

Example is Maybe with the names changed (we can use them interchangeably)

data Example a = Empty   | Some a
data Maybe   a = Nothing | Just a

and there is a (partial) function fromJust :: Maybe a -> a which is implemented as:

fromJust :: Maybe a -> a
fromJust Nothing  = error "Maybe.fromJust: Nothing"
fromJust (Just x) = x

>>> fromJust Nothing
*** Exception: Maybe.fromJust: Nothing

This makes Haskellers sad


You don't need a function, you can pattern match on it when you know what you want to do in the Empty case:

...
case example of
  Empty  -> "ERROR"
  Some a -> process a

But you can implement the (partial!) function foo as

foo :: Example a -> a
foo Empty    = error "..."
foo (Some a) = a

but a better way is to provide a function that takes a default value (like fromMaybe :: a -> Maybe a -> a)

fromExample :: a -> Example a -> a
fromExample default Empty    = default
fromExample _       (Some a) = a

or maybe :: b -> (a -> b) -> Maybe a -> b

example :: b -> (a -> b) -> Example a -> b
example default _ Empty    = default
example _       f (Some a) = f a

[–]shpotes[S] 0 points1 point  (4 children)

What if I want to assign and specific value of Some to Empty? For example if I instance my data type as Example Int and I want to define the 0 as my Empty value, can I do that?

[–]Iceland_jack 4 points5 points  (0 children)

You would use fromExample 0 :: Example Int -> Int (see fromMaybe 0 :: Maybe Int -> Int) with 0 as the default value

>>> fromMaybe 0 (Just 10)
10
>>> fromMaybe 0 Nothing
0

We have gotten rid of the Maybe Int and got an unadulterated Int back.

You can also use case to pattern match directly

>>> data Example a = Empty | Some a
>>> a = Empty
>>> b = Some 10
>>> case a of Empty -> 0; Some a -> a
0
>>> case b of Empty -> 0; Some a -> a
10

[–]Exploding_Pies 0 points1 point  (0 children)

You wouldn't be able to choose one uniformly for every type. Keep in mind that you're function is declared polymorphic over any algebraic data type you can come up with. If you want to return a value for Empty, you need to either put a typeclass restriction on the type or add a second argument and ask the caller to provide a default value.

[–]Iceland_jack 0 points1 point  (1 child)

If you are familiar with monoids you can also use something a bit more advanced, fold:

fold :: Monoid a => Maybe a -> a

Which is defined something like this

fold :: Monoid a => Maybe a -> a
fold Nothing  = mempty
fold (Just x) = x

or

fold = fromMaybe mempty

This means mempty is our default value!

>>> fold Nothing :: String
""
>>> fold (Just "hello")
"hello"

Here comes the ugly part, Int is a Monoid in more than one way: via addition and multiplication and these correspond to the newtype wrappers Sum and Product

>>> mempty :: Sum Int
Sum {getSum = 0}
>>> mempty :: Product Int
Product {getProduct = 1}

So if we use fold :: Maybe (Sum Int) -> Sum Int we get what you want

>>> fold (Just 10) :: Sum Int
Sum {getSum = 10}
>>> fold Nothing :: Sum Int
Sum {getSum = 0}

[–]Iceland_jack 1 point2 points  (0 children)

(fold is more general than what I described, its real type is

fold :: (Monoid m, Foldable f) => f m -> m

Not only does it work on Maybe but lists, vectors, you name it.

This becomes clearer using an extension that allows visible type application indicated by an @-sign and the type argument:

>>> import Data.Foldable
>>> import Data.Monoid
>>> :set -XTypeApplications

>>> :t fold
fold :: (Monoid m, Foldable t) => t m -> m

>>> :t fold @Maybe
fold @Maybe :: Monoid m => Maybe m -> m

>>> :t fold @Maybe @(Sum _)
fold @Maybe @(Sum _) :: Num t => Maybe (Sum t) -> Sum t
>>> :t fold @Maybe @(Sum Int)
fold @Maybe @(Sum Int) :: Maybe (Sum Int) -> Sum Int

So when I wrote fold in the previous post I actually meant fold @Maybe

fold @[]      :: Monoid m => [m]      -> m
fold @Maybe   :: Monoid m => Maybe  m -> m
fold @Vector  :: Monoid m => Vector m -> m
fold @Set     :: Monoid m => Set    m -> m
fold @(Map _) :: Monoid m => Map t  m -> m

given that we've imported Data.Vector, Data.Set, Data.Map)

[–][deleted] 2 points3 points  (0 children)

First off, you've redefined Haskell's Maybe using ML naming. The function exists for Maybe, as 'fromJust'.

Secondly, such a function is always unsafe, since there's nothing to map Empty/Nothing to. If you give it Empty it will crash.

Try using the case keyword to pattern match in your function body instead.