Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to apply Maybe constructor to each field of record with generics?

I have two data types and the second one is the copy of first, but with Maybe on each field.

data A = {a :: Int, b :: String}
data B = {c :: Maybe Int, d :: Maybe String}

Is there a way to make a functions

f :: A -> B
g :: B -> A -> A

without any knowledge about fields itself? (if value of first argument is nothing g will take default value from second argument)

like image 232
meownoid Avatar asked Dec 24 '22 02:12

meownoid


1 Answers

This can be done with generics-sop, a library that extends the default Generics machinery of GHC.

"generics-sop" can take a regular record and deduce a generic representation for it. This representation has a type parameter that wraps every field, and the library allows Applicative sequence-like operations across the record fields.

{-# language TypeOperators #-}
{-# language DeriveGeneric #-}
{-# language TypeFamilies #-}
{-# language DataKinds #-}

import qualified GHC.Generics as GHC
import Generics.SOP

data A = A {a :: Int, b :: String} deriving (Show,GHC.Generic)

instance Generic A -- this Generic is from generics-sop

defaulty :: (Generic a, Code a ~ '[ xs ]) => NP Maybe xs -> a -> a 
defaulty maybes r = case (from r) of
    SOP (Z np) -> let result = hliftA2 (\m i -> maybe i I m) maybes np
                  in  to (SOP (Z result))

main :: IO ()
main = do
   print $ defaulty (Nothing :* Just "bar" :* Nil) (A 99 "foo") 

Nothing :* Just "bar" :* Nil is a generic representation that matches the list of fields in the original record definition. Notice that each field in the representation is wrapped in Maybe.

See here for another example of generics-sop.

like image 140
danidiaz Avatar answered Jun 02 '23 10:06

danidiaz