Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get textual representation of record field?

Having the following record:

data Sleep = Sleep
   { _duration :: Maybe Int
   , _drunk :: Bool
   }

Is there a way to do the following:

deriveSomething ''Sleep

fieldName duration :: String -- "duration"

I need this for typesafe DB specific field updates, i.e:

setField connection key duration (Just 50)

It needs to be DB-agnostic though (thus opaleye etc. is out).

(If this can be achieved with a standard package like lens even better, but I wasn't able to find anything.)

like image 705
Philip Kamenarsky Avatar asked Jan 31 '26 11:01

Philip Kamenarsky


1 Answers

You can do this using Data.Data:

{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data

data Sleep = Sleep
    { _duration :: Maybe Int
    , _drunk :: Bool
    } deriving (Typeable, Data)

fieldNames :: Data a => a -> [String]
fieldNames = constrFields . toConstr

Example:

> fieldNames (Sleep undefined undefined)
["_duration", "_drunk"]

You'll have to decide how you want to transform the names to database columns after that, but that should be fairly easy.

This requires a value created using a particular constructor, remember that data types can have many constructors. There isn't really a way around this, but you could have something like

sleepFieldNames :: [String]
sleepFieldNames = fieldNames (Sleep undefined undefined)

So that you don't have to keep recalculating it.

like image 160
bheklilr Avatar answered Feb 03 '26 02:02

bheklilr