All Projects → kayhide → Wakame

kayhide / Wakame

Licence: bsd-3-clause
Haskell library of row polymorphic record manipulator

Programming Languages

haskell
3896 projects

Projects that are alternatives of or similar to Wakame

30 Seconds Of Cpp
30 Seconds of C++ (STL in C++). Read More about 30C++ here 👉
Stars: ✭ 815 (+4993.75%)
Mutual labels:  hacktoberfest
Javascript Algorithms
Stars: ✭ 6,893 (+42981.25%)
Mutual labels:  hacktoberfest
Pact Jvm
JVM version of Pact. Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.
Stars: ✭ 833 (+5106.25%)
Mutual labels:  hacktoberfest
Node Influx
📈 The InfluxDB Client for Node.js and Browsers
Stars: ✭ 820 (+5025%)
Mutual labels:  hacktoberfest
Guess
🔮 Libraries & tools for enabling Machine Learning driven user-experiences on the web
Stars: ✭ 6,762 (+42162.5%)
Mutual labels:  hacktoberfest
Js Interview Questions
❔❓❔ Notes from technical (javascript) interviews. Tasks and quiz for different topics to discuss on interview / check self skills in javascript
Stars: ✭ 830 (+5087.5%)
Mutual labels:  hacktoberfest
Kubernetes Examples
Minimal self-contained examples of standard Kubernetes features and patterns in YAML
Stars: ✭ 811 (+4968.75%)
Mutual labels:  hacktoberfest
Abapgit
Git client for ABAP
Stars: ✭ 835 (+5118.75%)
Mutual labels:  hacktoberfest
Sfml
Simple and Fast Multimedia Library
Stars: ✭ 7,316 (+45625%)
Mutual labels:  hacktoberfest
Django Rest Framework Gis
Geographic add-ons for Django REST Framework. Maintained by the OpenWISP Project.
Stars: ✭ 830 (+5087.5%)
Mutual labels:  hacktoberfest
Aws Toolkit Vscode
AWS Toolkit for Visual Studio Code, an extension for working with AWS services including AWS Lambda.
Stars: ✭ 823 (+5043.75%)
Mutual labels:  hacktoberfest
Ciphey
⚡ Automatically decrypt encryptions without knowing the key or cipher, decode encodings, and crack hashes ⚡
Stars: ✭ 9,116 (+56875%)
Mutual labels:  hacktoberfest
Apispec
A pluggable API specification generator. Currently supports the OpenAPI Specification (f.k.a. the Swagger specification)..
Stars: ✭ 831 (+5093.75%)
Mutual labels:  hacktoberfest
Lychee
A great looking and easy-to-use photo-management-system you can run on your server, to manage and share photos.
Stars: ✭ 816 (+5000%)
Mutual labels:  hacktoberfest
Azure Sdk For Java
This repository is for active development of the Azure SDK for Java. For consumers of the SDK we recommend visiting our public developer docs at https://docs.microsoft.com/en-us/java/azure/ or our versioned developer docs at https://azure.github.io/azure-sdk-for-java.
Stars: ✭ 834 (+5112.5%)
Mutual labels:  hacktoberfest
Galaxy
Data intensive science for everyone.
Stars: ✭ 812 (+4975%)
Mutual labels:  hacktoberfest
Automatic Ripping Machine
Automatic Ripping Machine (ARM) Scripts
Stars: ✭ 827 (+5068.75%)
Mutual labels:  hacktoberfest
Wwcode Hacktoberfest 2020
🌎 No intuito de colaborar e incentivar a participação de mais mulheres na comunidade open source , um pull request por vez, nós do Women Who Code Recife, criamos um projeto para participarmos desta causa incrível, e toda comunidade poder colaborar
Stars: ✭ 16 (+0%)
Mutual labels:  hacktoberfest
Liquidctl
Cross-platform CLI and Python drivers for AIO liquid coolers and other devices
Stars: ✭ 830 (+5087.5%)
Mutual labels:  hacktoberfest
Jackson Module Kotlin
Module that adds support for serialization/deserialization of Kotlin (http://kotlinlang.org) classes and data classes.
Stars: ✭ 830 (+5087.5%)
Mutual labels:  hacktoberfest

Wakame

GitHub CI Hackage Stackage LTS Stackage Nightly BSD3 license

wakame is a Haskell library to manipulate record fields in a row-polymorphic way.

Overview

Here is a quick overview of what wakame provides.

Imagine a data type of:

data User =
  User
  { id :: ID User
  , email :: Text
  , username :: Text
  , created_at :: UTCTime
  , updated_at :: UTCTime
  }
  deriving Generic

To update a subset of the User record's fields, first define a data type containing the fields you want to update:

data UpdatingUser =
  UpdatingUser
  { email :: Text
  , username :: Text
  }
  deriving Generic

Then, write a function for doing the update:

updateUser :: UpdatingUser -> User -> User
updateUser updating user = fromRec $ nub $ union (toRow updating) (toRow user)

Here is a working example of using this function:

> user
User {id = ID 42, email = "[email protected]", username = "Peter Parker", created_at = 2020-06-16 11:22:11.991147596 UTC, updated_at = 2020-06-16 11:22:11.991147596 UTC}
> updating
UpdatingUser {email = "[email protected]", username = "Spider Man"}
> updateUser updating user
User {id = ID 42, email = "[email protected]", username = "Spider Man", created_at = 2020-06-16 11:22:11.991147596 UTC, updated_at = 2020-06-16 11:22:11.991147596 UTC}

Updating the updated_at field in User can be done in the same manner. But this time, let's do it without defining a separate record type:

touchUser :: UTCTime -> User -> User
touchUser time user = fromRec $ nub $ union (toRow $ keyed @"updated_at" time) (toRow user)

toRow $ keyed @"update_at" time creates a Row object which has only one field:

{ updated_at :: UTCTime }

And updating the user and the updated_at field can be done easily within the same function:

updateAndTouchUser :: UpdatingUser -> UTCTime -> User -> User
updateAndTouchUser updating time user =
  fromRec $ nub $ union (toRow $ updating) $ union (toRow $ keyed @"updated_at" time) (toRow user)

This function works as follows:

> updateAndTouchUser updating time user
User {id = ID 42, email = "[email protected]", username = "Spider Man", created_at = 2020-06-16 11:22:11.991147596 UTC, updated_at = 2020-06-16 11:31:35.170029827 UTC}

Note that using nub once after a chain of unions will be faster than using nub after every individual union.

Wrapping up, we have done the following:

  • Converting a record into its corresponding Row representation with the toRow function
  • Adding, removing or replacing the fields over the Row with union and nub
  • Converting back to a record with fromRow

Row-polymorphic functions

The following create and update functions are generalized in terms of row-polymorphism.

data ModelBase a =
  ModelBase
  { id         :: ID a
  , created_at :: UTCTime
  , updated_at :: UTCTime
  }
  deriving (Eq, Show, Generic)


create ::
  forall a b.
  ( IsRow a
  , IsRow b
  , Lacks "id" (Of a)
  , Merge (Of a) (Of (ModelBase b)) (Of b)
  ) => a -> IO b
create x = do
  now <- getCurrentTime
  id' <- pure $ ID @b 42 -- shall be `getNextID` or something in practice.
  let y =
        fromRow
        $ merge (toRow x)
        $ toRow $ ModelBase @b id' now now
  pure y


type OfUpdatedAt = '[ '("updated_at", UTCTime) ]

update ::
  ( IsRow a
  , IsRow b
  , Union (Of a) (Of b) ab
  , Merge OfUpdatedAt ab (Of b)
  ) => a -> b -> IO b
update updating x = do
  now <- getCurrentTime
  let y =
        fromRow
        $ merge (toRow $ keyed @"updated_at" now)
        $ union (toRow updating)
        $ toRow x
  pure y
  • IsRow is a constraint which defines the Of type family and a pair of toRow / fromRow functions.
    • wakame defines an instance of IsRow for all Haskell records with a Generic instance.
  • Lacks constrains a row to not have a field with the given label.
  • Merge is a combination of Union and Nub, which do appending and removing respectively.

With these constraints and functions, you can easily write row polymorphic functions in your application.

These examples are found at Wakame.Examples.Usage.

There are other examples available at Wakame.Examples.Functons.

If you're interested in row polymorphism, the Wikipedia page may help: Row polymorphism.

A direct translation of the functions described in the Wikipedia page is also available at Wakame.Examples.RowPolymorphism.

Underlying data structure

wakame uses NP (a.k.a. "N-ary Product") as the underlying representation of Row. NP is a data type from the sop-core library.

So if you need finer control of Row, or if you need an advanced or application-specific operation, you have the option of using the NP data type directly, which will allow you to take advantage of the rich set of functions from the sop-core library.

For more details, see the paper True Sums Of Products.

Why not record-sop ?

records-sop is a library built on top of sop-core. It focuses on the representation of a record data type and provides a set of functions for doing conversions.

The difference is that records-sop is relying on generics-sop which is more general and also covers non-record data types. wakame is specialized for only record data types.

Although the representation data types is virtually the same between records-sop and wakame, how to convert between data types is different.

One of the benefits of wakame is the ability to introduce special conversion rules such as keyed @"label" value to / from Row.

wakame gives you the ability to make a single keyed value correspond to the representation of a data type with one field, and any arbitrary tuple of keyed values to a data type with multiple fields. In this way, you can use a tuple of keyed values in place of an anonymous record.

What is wakame?

Wakame is a type of edible seaweed, popular in Japan.

The most important property of wakame is that, it changes its color when boiled.

Contributions

Feel free to open an issue or PR. Thanks!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].