Coder Social home page Coder Social logo

haskell-total-library's Introduction

total

The total library lets you exhaustively pattern match on types using Traversals, Prisms, and Lenses.

Quick Start

Example code:

import Lens.Family.Total
import Lens.Family.Stock

total :: Either Char Int -> String       -- Same as:
total = _case                            -- total = \case
    & on _Left  (\c -> replicate 3  c )  --     Left  c -> replicate 3 c
    & on _Right (\n -> replicate n '!')  --     Right n -> replicate n '!'

To learn more, read the documentation

Development Status

Build Status

I don't foresee any additions to the library, so it's probably stable. Only time will tell.

LICENSE

Copyright (c) 2015 Gabriella Gonzalez All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Gabriella Gonzalez nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

haskell-total-library's People

Contributors

cschneid avatar echatav avatar gabriella439 avatar markus1189 avatar masaeedu avatar treeowl avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

haskell-total-library's Issues

How to use _2?

The example shows that you can use _1 https://github.com/Gabriel439/Haskell-Total-Library/blob/master/src/Lens/Family/Total.hs#L73-L77
But trying something similar with _2 doesn't seem to work.

module Tuple where

import Lens.Family.Total
import Lens.Family.Stock

foo :: (Int, Bool) -> String
foo = _case
    & on _2 show

The error is:

➜  totally  cabal build
Building totally-0.1.0.0...
Preprocessing library totally-0.1.0.0...
[1 of 1] Compiling Tuple            ( src/Tuple.hs, dist/build/Tuple.o )

src/Tuple.hs:7:7:
    No instance for (Empty Int) arising from a use of ‘_case’
    In the first argument of ‘(&)’, namely ‘_case’
    In the expression: _case & on _2 show
    In an equation for ‘foo’: foo = _case & on _2 show

It seems like it might be because the tuple instance is only defined for instance Empty a => Empty ((,) a b) ...

ghc takes it as a duplicate instance if I attempt to define this instance in addition:

instance Empty b => Empty ((,) a b) where
    impossible (_, b) = impossible b
➜  Haskell-Total-Library git:(master) ✗ cabal build
Building total-1.0.1...
Preprocessing library total-1.0.1...
[1 of 1] Compiling Lens.Family.Total ( src/Lens/Family/Total.hs, dist/build/Lens/Family/Total.o )

src/Lens/Family/Total.hs:124:10:
    Duplicate instance declarations:
      instance Empty a => Empty (a, b)
        -- Defined at src/Lens/Family/Total.hs:124:10
      instance Empty b => Empty (a, b)
        -- Defined at src/Lens/Family/Total.hs:127:10

And attempting to have both parts be constraints forces you to match all cases in the function (which isn't as intended.)

Any ideas what can be done about this? Is it something simple I'm missing?

Default if no match

Total idiomatically supports defaulting if there is no match using const default or \_ -> default instead of _case like so:

data Example a b c = C1 a | C2 b | C3 c deriving (Generic)

makePrisms ''Example

instance (Empty a, Empty b, Empty c) => Empty (Example a b c)

getString :: Example String Double Int -> String
getString = const "not a string"
    & on _C1 (\s -> s)

Maybe this should be documented? Maybe a synonym _default = const?

Slightly generalize

We have

at
    :: ((() -> Identity b) -> s -> Identity t)
    -> (i -> b)
    -> (i -> s)
    -> i
    -> t

But () isn't really the only reasonable target type. We could have, for example,

data Rec1 a b = Rec1 { _foo :: a, _bar :: b }
data Rec2 a b c = Rec2 { _baz :: Rec1 a b, _quux :: c }

There are two different reasonable ways to build a Rec2 starting with Rec2 () () ():

  1. Use baz . foo, baz . bar, and quux to build from an a, a b, and a c.
  2. Use baz and quux to build from a Rec1 a b and a c.

at currently only supports option 1. We can support option 2 using this type:

atg
    :: Full u => ((u -> Identity b) -> s -> Identity t)
    -> (i -> b)
    -> (i -> s)
    -> i
    -> t

Should the type of at be changed, or should a new function be added with the more general type?


The situation for on is similar, I believe. Suppose we have

data Sum1 a b = A a | B b
data Sum2 a b c = AB (Sum1 a b) | C c

We currently support matching on Sum2 using _AB . _A, _AB . _B, and _C, but not using _AB with _C. The target of _AB just can't be made Void; it needs to be made Sum1 Void Void.

We should be able to fix this the same way:

ong
    :: Empty v
    => ((a -> Either a v) -> s -> Either a r)
    -> (a -> o)
    -> (r -> o)
    -> s
    -> o

Cototal copatterns

In Haskell we have patterns and pattern matching for deconstructing sum types. In some languages, there is also the dual concept of a "copattern" for building up product types.

Since your idea makes pattern matching "first class", and hence independent of the syntactic features of the language, the logical next step is to dualize the implementation and end up with first class copatterns!

Total patterns:

module Total where

import Data.Void
import Data.Function ((&))

import Lens

class Empty a
  where
  impossible :: a -> x

instance Empty Void
  where
  impossible = absurd

instance (Empty a, Empty b) => Empty (Either a b)
  where
  impossible = either impossible impossible

_case :: Empty a => a -> x
_case = impossible

on :: Prism s t a Void -> (a -> o) -> (t -> o) -> s -> o
on (Prism _ m) l r = either r l . m

total :: Either Char Int -> String     -- Same as:
total = _case                          -- total = \case
  & on _Left  (\c -> replicate 3  c )  --   Left  c -> replicate 3 c
  & on _Right (\n -> replicate n '!')  --   Right n -> replicate n '!'

Cototal copatterns:

module Cototal where

import Data.Function ((&))
import Control.Arrow ((&&&))
import Data.Maybe (listToMaybe)

import Lens

class Full a
  where
  trivial :: x -> a

instance Full ()
  where
  trivial = const ()

instance (Full a, Full b) => Full (a, b)
  where
  trivial = trivial &&& trivial

_cocase :: Full a => x -> a
_cocase = trivial

at :: Lens s t () b -> (i -> b) -> (i -> s) -> i -> t
at (Lens _ s) l r = s . (r &&& l)

cototal :: String -> (Maybe Char, Int)
cototal = _cocase
  & at _1 listToMaybe
  & at _2 length

Empty and Full

This instance feels like it should exist, though I don't know if it's useful in context:

instance (Full a, Empty b) => Empty (a -> b) where
  impossible f = impossible (f (trivial ()))

Unfortunately, the class system isn't really up to the task of Full (a -> b), since we'd want both instance Full b => Full (a -> b) and instance Empty a => Full (a -> b). I think there are some tricks we could play with type families and incoherent instances, but it's probably better not to.

Relax upper-bound on ghc-prim

From what I've tested, the total library builds fine using ghc-prim-0.4.0.0, which is required by base-4.8.0.0, ie GHC 7.10 .
Would you please consider relaxing the current < 0.4 bound on ghc-prim ?

Thank you.

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.