Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check if all of the Maybe fields in a haskell record are nothing?

Tags:

haskell

If I had a record like:

data MaybeTest = MaybeTest {
  test0 :: String
  test1 :: Maybe Int,
  test2 :: Maybe Float,
  test3 :: Maybe Int,
  test4 :: Maybe String,
  test5 :: Maybe String
  }
  deriving (Typeable, Data, Eq, Show)     

is there a simple way to see if all the maybe fields are Nothing? I would like to return Nothing if the records fit this condition else return Just MaybeTest. I want to avoid my current long method of calling each Maybe field and checking if they are Nothing.

like image 337
Alex Chen Avatar asked Jan 01 '23 00:01

Alex Chen


1 Answers

Stuff like this usually requires generics. Data.Data generics are generally the easiest to use. You can define a query that checks all Maybe x fields with the isNothing predicate using:

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Maybe (isNothing)
import Data.Data (Data, gmapQ)
import Data.Generics.Aliases (ext1Q)

allNothing :: (Data d) => d -> Bool
allNothing = and . gmapQ (const True `ext1Q` isNothing)

Here, ext1Q constructs a query that applies isNothing to any type matching Maybe b while applying const True to any non-Maybe type. The gmapQ function maps it across all fields of the target type, resulting in a list of booleans that are True for all non-Maybe types and Nothing values but False for all Just x values.

You can test it like so:

data Foo = Foo String (Maybe Int) Char (Maybe Double) deriving (Show, Data)
data Bar = Bar (Maybe [Int]) deriving (Show, Data)

and in GHCi:

λ> allNothing (Foo "x" Nothing 'a' Nothing)
True
λ> allNothing (Foo "x" Nothing 'a' (Just 3.14))
False
λ> allNothing (Bar Nothing)
True
λ> allNothing (Bar (Just [1..10]))
False

With that function available, it's easy to write your desired function:

checkNothing :: (Data d) => d -> Maybe d
checkNothing x | allNothing x = Nothing
               | otherwise    = Just x

giving:

λ> checkNothing (Foo "x" Nothing 'a' Nothing)
Nothing
λ> checkNothing (Foo "x" Nothing 'a' (Just 3.14))
Just (Foo "x" Nothing 'a' (Just 3.14))
like image 187
K. A. Buhr Avatar answered Jan 02 '23 13:01

K. A. Buhr