Reputation: 3636
Is there a function in haskell that generalizes the maybe and either functions?
For instance, I am imagining a function like this:
generalizedFunc :: SOMETHING m => b -> (a -> b) -> m a -> b
generalizedFunc valForError f value = ...
Working with it in ghci would look like this:
> generalizedFunc "bye" (\_ -> "hello") (Just 3)
"hello"
> generalizedFunc "bye" (\_ -> "hello") Nothing
"bye"
> generalizedFunc "bye" (\_ -> "hello") (Right 3)
"hello"
> generalizedFunc "bye" (\_ -> "hello") (Left "error")
"bye"
note: Tom Ellis makes a good point that this is not a generalization of Either, but rather a specialization.
Upvotes: 5
Views: 364
Reputation: 53901
I guess this is a little bit of self promotion but I'll post it because it's very relevant.
I wrote a small library a while back called generic-church. This exports a single type class with two functions. To use it, simply do something like
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
import Data.Church
data Nat = Z | S Nat
instance ChurchRep Nat
Notice that Nat
need not be a functor as with Foldable
.
We can then fire up GHCi and do something like
*> (toChurch Z) 'a' (const 'b')
'a'
*> (toChurch $ S Z) 'a' (const 'b')
'b'
And unlike foldr
you can automagically turn a church representation back into a concrete data type
*> fromChurch (\a b -> a) :: Nat
Z
*> fromChurch (a b -> b Z) :: Nat
S Z
Now one obvious downside as compared to catamorphisms/folds is that generic-church
currently handles recursive data types in a slightly naive way (though I'm looking to fix this Real Soon Now). Namely, it treats any recursive type as though it where unfolded one level in it's church representation, so the church representation of Nat
is
type Nat' = forall c. c -> (Nat -> c) -> c
generalFunc
Here's the code to write your generalFunc
in a manner which actually generalizes maybe
. I need a function toChurchP
which is in generic-church-0.2 uploaded about 5 minutes prior to me typing this. If you want to run this code be sure to use cabal update
:)
{-# LANGUAGE DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, TypeFamilies, ScopedTypeVariables #-}
module So where
import Data.Church
import GHC.Generics
import Data.Proxy
-- Generalize of const. Allows us to use more than just Either
-- for gmaybe
class GConst a r where
gconst :: r -> a
instance GConst r' r => GConst (a -> r') r where
gconst r = const (gconst r)
instance GConst r r where
gconst = id
gfail :: forall a e s e' r.
(ChurchRep a, GConst e' e, Church a r ~ (e' -> s -> r)) =>
e -> s -> a -> r
gfail e s a = toChurchP (Proxy :: Proxy r) a (gconst e :: e') s
This depends on the that gconst
to swallow all arguments given to the failure result, e
. A clever definition should allow something like
> gfail id (const 'a') $ Left 'b'
'b'
But my current formulation would require -XIncoherentInstances
(eek!). I'll hopefully update with a version that would actually generalize both either
and maybe
.
As it is this is still kinda nifty.
*So> gfail True id (Just False)
False
*So> gfail True (id :: Bool -> Bool) Nothing
True
*So> gfail 'a' (const 'b') (Left ())
'a'
*So> gfail 'a' (const 'b') (Right ())
'b'
*So> :set -XDeriveGeneric
*So> data MyMaybe = Fail | Yay Int deriving (Show, Generic)
*So> instance ChurchRep MyMaybe
*So> gfail 'a' (const 'b') (Yay 1)
'b'
Upvotes: 3
Reputation:
Yes. What you're looking for is Data.Foldable
. It generalizes foldr
(for lists) to any algebraic data type:
Data.List.foldr :: (a -> b -> b) -> b -> [] a -> b
maybe :: b -> (a -> b) -> Maybe a -> b
either :: (a -> c) -> (b -> c) -> Either a b -> c
---
Data.Foldable.foldr :: Foldable t
=> (a -> b -> b) -> b -> t a -> b
Your code would change from generalizedFunc "bye" (\_ -> "hello")
to foldr (\_ _ -> "hello") "bye"
. Make sure you tell the compiler that you mean the foldr
from Data.Foldable
; see the documentation of the module.
Unfortunately, the Foldable (Either a)
instance might be missing for your version of GHC, but writing one yourself should be relatively easy.
Upvotes: 6