Generate PureScript Knowledge Varieties From

Ong Yi Ren
Fullstack with two purely Useful Programming Language

The necessity arose after I was writing a full stack net utility with PureScript being the frontend and Haskell being the backend. Whereas these two languages are largely comparable, the truth that they’re completely different implies that one can not share the codes instantly between them. For all knowledge sorts outlined in Haskell, I’ve to rewrite them in PureScript. This seemes to be an automatable course of so I made a decision to discover a solution to let the pc do the work for me! Let’s begin with my most popular means of structuring knowledge sorts in each languages.

Coping with knowledge sorts in PureScript fairly easy due to its help of row kind polymorphism. You would all the time outline the info sorts as comply with

kind Person = { title :: String, deal with :: Metropolis }
kind Metropolis = { title :: String, postalCode :: Int }
x = { title : "Richard" , deal with : y }
y = { title : "Singapore" , postalCode : 10000 }

and entry the postalCode of x utilizing dot notation with.postalCode, identical as what you’ll usually do in most OOP language.Updating a area can also be easy, by writing z = x { deal with { postalCode = 20000 }}, you can create a brand new z with the identical property as x ,however with a postalCode worth of 20000 .

In distinction, row kind polymorphism isn’t supported in Haskell by default. In Haskell you aren’t in a position to outline duplicate file fields in the identical module. If you happen to attempt to outline the next in the identical module, it gained’t compile.

-- kind Person = { title :: String , deal with :: Metropolis }
-- Not doable to outline knowledge kind as above in Haskell, as an alternative we should outline knowledge as comply with
-- Will not work as a result of we have now duplicate file area "name"knowledge Person = Person { title :: String, deal with :: Metropolis}
knowledge Metropolis = Metropolis { title :: String, postalCode :: Int }

Due to this, it is not uncommon so as to add Hungarian Notation in entrance of Haskell file fields.

knowledge Person = Person { uName :: String, uAddress :: Metropolis}
knowledge Metropolis = Metropolis { cName :: String, cPostalCode :: Int }
x = Person { uName = "Richard" , uAddress = y }
y = Metropolis { cName = "Singapore" , cPostalCode =10000}

To entry the cPostalCode of x , you may’t use the dot notation, you would wish to both do sample matching or use the accessor operate.

-- Use sample matching postalCode :: Int
postalCode = case x of
Person { uName = _ , uAddress = Metropolis { cName = _ , cPostalCode = y}} - y
-- Use accessor operatepostalCode :: Int
postalCode = (cPostalCode . uAddress) x

To create a brand new z with completely different cPostalCode from x

-- Use sample matching z :: Person 
z = case x of
Person { uName = a , uAddress = Metropolis { cName = b, cPostalCode = _ }} - Person { uName = a , uAddress = Metropolis { cName = b, cPostalCode = 20000 }}
-- Use accesor operate z :: Person
z = x { uAddress = (uAddress x) { cPostalCode = 20000 }}

As you would possibly discover, each prepending of Hungarian Notation and the best way of getting/updating file fields can get ugly actual fast. One of many answer to those is to make use of GHC extension DuplicateRecordFields for the previous and lens for the latter. This article summarizes the approach intimately.

Now let’s discuss concerning the necessities.I need the pc to auto generate most knowledge sorts and kind class situations for me as a result of writing them manually is so boring…

1. No fancy sorts. Even when they exist, they’d be minimal and I gained’t thoughts rewriting them manually however majorify of my knowledge sorts are simply easy knowledge sorts.
2. Choose to make the most of row kind polymorphisms in Haskell in order that I might generate one thing like kind Person = { title :: String , deal with :: Metropolis} in PureScript.There are probaby libraries/GHC extensions which might assist me to realize that.
3. Auto generate customary kind class’s situations such Eq and Ord.
4. Auto generate JSON encode/decode occasion.
5. Auto generate optics situations reminiscent of Lens , Prisms and Iso.
6. Minimal effort, I’d not be capable to write a library from scratch, it will be extra possible to leverage present library every time doable.
7. My haskell code must convert third social gathering JSON knowledge into Haskell knowledge sorts, as a result of I exploit the method I discussed earlier , I want to incorporate the choice fieldLabelModifier = drop 1 in Aeson TH to drop the underscore prefix with the intention to match the JSON knowledge.

-- Given a json knowledge {"name": "Richard","city" :{"name": "Singapore", "postalCode": "10000"}. 
-- I must prepend my file area with _ to make use of makeFieldsNoPrefix, nonetheless if I derive JSON instantly with out dropping the underscore prefix, the end result could be {"_name": "Richard","_city" :{"_name": "Singapore", "_postalCode": "10000"}}
knowledge Person = Person { _name :: String
, _address :: Metropolis
} deriving (Eq,Generic)
knowledge Metropolis = Metropolis { _name :: String
, _postalCode :: Int
} deriving (Eq,Generic)
makeFieldsNoPrefix ''Person
makeFieldsNoPrefix ''Metropolis
-- "drop 1" is to drop the underscore prefixderiveJSON defaultOptions {fieldLabelModifier = drop 1 } ''Person
deriveJSON defaultOptions {fieldLabelModifier = drop 1 } ''Metropolis

First try
I began by trying to find a solution to make the most of row kind polymorphism in Haskell. Here is a listing of libraries which help extensible information and/or extensible variants.There have been three considerations for me.
1. Some libraries appear to be proof of idea and should not being maintained, reminiscent of extensible-data and named-records.
2. Some libraries are effectively maintained however are technically difficult for me, reminiscent of vinyl and row-type.
3. None of them has present libraries which generate PureScript knowledge sorts.

Second try
I discovered a pleasant Haskell library purescript-bridge which generate PureScript knowledge sorts from widespread Haskell knowledge sorts. Does it imply the tip of story? Probably not. It is because the prevailing library doesn’t help a performance which might modify the file area when encoding/decoding JSON, that is wanted by my seventh requirement . So I dived into the underlying PureScript library purescript-foreign-generic and located that there’s an possibility fieldTransform which is anologous to fieldLabelModifier in Aeson TH, this appears to be a easy repair; by including an possibility within the library, I’m able to drop the underscore prefix. Nevertheless, the library purescript-bridge generates Lens situations for knowledge sorts and finally ends up producing duplicate operate title after I use the identical file fields in two completely different Haskell knowledge sorts.

-- Knowledge kind definition in Haskellknowledge Person = Person { _name :: String
, _address :: Metropolis
} deriving (Eq,Generic)
knowledge Metropolis = Metropolis { _name :: String
, _postalCode :: Int
} deriving (Eq,Generic)
makeFieldsNoPrefix ''Person
makeFieldsNoPrefix ''Metropolis
-- Generated Code in PureScriptnewtype Person = Person { _name :: String , _address :: Metropolis }
newtype Metropolis = Metropolis { _name :: String , _postalCode :: Int}
.... Skippedtitle :: Lens' Person title
title = _Newtype <<< prop (SProxy :: SProxy "_name")"
-- Duplicate function namename :: Lens' City name
name = _Newtype <<< prop (SProxy :: SProxy "_name")"

At this level, I ought to have used the ultimate try to unravel the problems however I wasn’t conscious of it.

Third try
I’m sharing the third try anyway. So I made a decision to stay with Hungarian Notation, to be exact, I made a decision to prepend the info constructor for all file fields. Within the purescript-bridge library , I added an choice to drop the info constructor prefix.

-- Knowledge kind definition in Haskellknowledge Person = Person { _userName :: String
, _userAddress :: Metropolis
} deriving (Eq,Generic)
knowledge Metropolis = Metropolis { _cityName :: String
, _cityPostalCode :: Int
} deriving (Eq,Generic)
deriveJSON defaultOptions {fieldLabelModifier = x - case stripPrefix "_user" x of
Simply y - toLower (take 1 y) drop 1 y
Nothing - x
} ''Person


Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: