GithubHelp home page GithubHelp logo

mpickering / generic-lens Goto Github PK

View Code? Open in Web Editor NEW

This project forked from kcsongor/generic-lens

0.0 3.0 0.0 674 KB

Generic datatype operations exposed as lenses.

License: BSD 3-Clause "New" or "Revised" License

Haskell 100.00%

generic-lens's Introduction

generic-lens

Build Status Hackage

Generically derive lenses and prisms for data types.

Available on Hackage

This package uses the GHC 8 Generic representation to derive various operations on data structures with lens interfaces, including structural subtype relationships between records and positional indexing into arbitrary product types.

This is made possible by GHC 8's new Generics API, which provides metadata at the type-level (previously only value-level metadata was available).

Examples can be found in the examples folder. This library makes heavy use of Visible Type Applications.

Lenses

Record fields

Record fields can be accessed by their label:

data Person = Person { name :: String, age :: Int } deriving (Generic, Show)

sally :: Person
sally = Person "Sally" 25
>>> getField @"age" sally
25

>>> setField @"age" 26 sally
Person {name = "Sally", age = 26}

>>> sally ^. field @"name"
"Sally"

>>> sally & field @"name" .~ "Tamas"
Person {name = "Tamas", age = 25}

>>> sally ^. field @"pet"
error:
   The type Person does not contain a field named "pet"

If the accessed field is a type parameter that appears uniquely in the type, then its type can be changed:

data T a b c d = T
  { paramA :: a
  , paramB :: b
  , paramC :: c
  , paramD :: d
  }
  deriving (Generic, Show)

>>> t = T "a" (10 :: Int) 'c' False
>>> t & field @"paramA" .~ 'a' & field @"paramB" .~ False
T {paramA = 'a', paramB = False, paramC = 'c', paramD = False}

Positional fields

Fields can be accessed by their position in the data structure (index starting at 1):

data Point = Point Int Int Int deriving (Generic, Show)
data Polygon = Polygon Point Point Point deriving (Generic, Show)

polygon :: Polygon
polygon = Polygon (Point 1 5 3) (Point 2 4 2) (Point 5 7 (-2))
>>> getPosition @2 polygon
Point 2 4 2

>>> setPosition @1 (Point 26 5 3) polygon
Polygon (Point 26 5 3) (Point 2 4 2) (Point 5 7 (-2))

>>> polygon ^. position @1 . position @2
5

>>> polygon & position @3 . position @2 %~ (+10)
Polygon (Point 1 5 3) (Point 2 4 2) (Point 5 17 (-2))

>>> polygon ^. position @10
error:
   The type Polygon does not contain a field at position 10

Since tuples are an instance of Generic, they also have positional lenses:

>>> (("hello", True), 5) ^. position @1 . position @2
True

Typed fields

Fields can be accessed by their type in the data structure, assuming that this type is unique:

data Person = Person { name :: String, age :: Int } deriving (Generic, Show)
data Point = Point Int Int Int deriving (Generic, Show)

sally :: Person
sally = Person "Sally" 25

point :: Point
point = Point 1 2 3
>>> getTyped @String sally
"Sally"

>>> setTyped @Int sally 26
Person {name = "Sally", age = 26}

>>> point ^. typed @Int
error:
   The type Point contains multiple values of type Int; the choice of value is thus ambiguous

>>> point & typed @String .~ "Point"
error:
   The type Point does not contain a value of type [Char]

Structural subtyping

A record is a (structural) `subtype' of another, if its fields are a superset of those of the other.

data Human = Human
  { name    :: String
  , age     :: Int
  , address :: String
  } deriving (Generic, Show)

data Animal = Animal
  { name    :: String
  , age     :: Int
  } deriving (Generic, Show)

human :: Human
human = Human {name = "Tunyasz", age = 50, address = "London"}
>>> upcast human :: Animal
Animal {name = "Tunyasz", age = 50}

-- 'smash' plug the smaller structure into the larger one
>>> smash (Animal "dog" 10) human
Human {name = "dog", age = 10, address = "London"}

-- 'super' is a lens that focuses on a subrecord of a larger record:
>>> human ^. super @Animal
Animal {name = "Tunyasz", age = 50}

We can apply a function that operates on a supertype to the larger (subtype) structure, by focusing on the supertype first:

growUp :: Animal -> Animal
growUp (Animal name age) = Animal name (age + 50)

>>> human & super @Animal %~ growUp
Human {name = "Tunyasz", age = 60, address = "London"}

Prisms

Named constructors

Constructor components can be accessed using the constructor's name:

type Name = String
type Age  = Int

data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
data Animal = Dog Dog | Cat Name Age | Duck Age deriving (Generic, Show)

shep = Dog (MkDog "Shep" 4)
mog = Cat "Mog" 5
donald = Duck 4
>>> shep ^? _Ctor @"Dog"
Just (MkDog {name = "Shep", age = 4})

>>> shep ^? _Ctor @"Cat"
Nothing

>>> mog ^? _Ctor @"Cat"
Just ("Mog",5)

>>> _Ctor @"Cat" # ("Garfield", 6) :: Animal
Cat "Garfield" 6

>>> donald ^? _Ctor @"Giraffe"
error:
   The type Animal does not contain a constructor named "Giraffe"

Typed constructors

Constructor components can be accessed using the component's type, assuming that this type is unique:

type Name = String
type Age  = Int

data Dog = MkDog { name :: Name, age :: Age } deriving (Generic, Show)
data Animal = Dog Dog | Cat (Name, Age) | Duck Age deriving (Generic, Show)

shep = Dog (MkDog "Shep" 4)
mog = Cat ("Mog", 5)
donald = Duck 4
>>> mog ^? _Typed @Dog
Nothing

>>> shep ^? _Typed @Dog
Just (MkDog {name = "Shep", age = 4})

>>> donald ^? _Typed @Age
Just 4

>>> donald ^? _Typed @Float
error:
   The type Animal does not contain a constructor whose field is of type Float

>>> _Typed @Age # 6 :: Animal
Duck 6

Contributors

generic-lens's People

Contributors

kcsongor avatar lunaris avatar tobyshaw avatar arianvp avatar nomeata avatar jonathanlking avatar mpickering avatar tebello-thejane avatar

Watchers

James Cloos avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.