newtype-generics (empty) → 0.4
raw patch · 5 files changed
+299/−0 lines, 5 filesdep +HUnitdep +basedep +hspecsetup-changed
Dependencies added: HUnit, base, hspec
Files
- Control/Newtype.hs +225/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- newtype-generics.cabal +40/−0
- test/main.hs +2/−0
+ Control/Newtype.hs view
@@ -0,0 +1,225 @@+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, TypeFamilies #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE UndecidableInstances #-}+{- |+The 'Newtype' typeclass and related functions: 'op', 'ala', 'ala'', 'under'.+Primarly pulled from Conor McBride's Epigram work. Some examples:++@ala Sum foldMap [1,2,3,4] -- foldMaps the list ala the Sum newtype. This results in 10.@++@ala Product foldMap [1,2,3,4] -- foldMaps the list ala the Product newtype. This results in 24.@++@ala Endo foldMap [(+1), (+2), (subtract 1), (*2)] 3 -- foldMaps the list ala the Endo newtype. This results in 8.@++NB: 'Data.Foldable.foldMap' is a generalized @mconcatMap@ which is a generalized @concatMap@.++This package includes 'Newtype' instances for all the (non-GHC\/foreign)+newtypes in base (as seen in the examples).+However, there are neat things you can do with this with+/any/ newtype and you should definitely define your own 'Newtype'+instances for the power of this library.+For example, see @ala Cont traverse@, with the proper 'Newtype' instance for Cont.++You can easily define new instances for your newtypes with the help of GHC.Generics++ > {—# LANGUAGE DeriveGeneric #—}+ > import GHC.Generics+ >+ > (...)+ > newtype Example = Example Int {deriving Generic)+ >+ > instance Newtype Example+ >++This avoids the use of Template Haskell (TH) to get new instances.+-}+module Control.Newtype+ ( Newtype(..)+ , op+ , ala+ , ala'+ , under+ , over+ , underF+ , overF+ ) where++import Data.Monoid+import Control.Applicative+import Control.Arrow+import GHC.Generics++-- | Given a newtype @n@, we will always have the same unwrapped type @o@,+-- meaning we can represent this with a fundep @n -> o@.+--+-- Any instance of this class just needs to let @pack@ equal to the newtype's+-- constructor, and let @unpack@ destruct the newtype with pattern matching.+{-class Newtype n o | n -> o where-}+ {-pack :: o -> n-}+ {-unpack :: n -> o-}+++-- Generic Newtype+class GNewtype n where+ type GO n :: *+ gpack :: GO n -> n p+ gunpack :: n p -> GO n++-- We only need one instance, if these generic functions are only to work for+-- newtypes, as these have a fixed form. For example, for a newtype X = Y,+-- Rep X = D1 ... (C1 ... (S1 ... (K1 ... Y)))+instance GNewtype (D1 d (C1 c (S1 s (K1 i a)))) where+ type GO (D1 d (C1 c (S1 s (K1 i a)))) = a+ gpack x = M1 (M1 (M1 (K1 x)))+ gunpack (M1 (M1 (M1 (K1 x)))) = x++-- Original Newtype class, extended with generic defaults (trivial) and deprived+-- of the second type argument (less trivial, as it involves a type family with+-- a default, plus an equality constraint for the related type family in+-- GNewtype). We do get rid of MultiParamTypeClasses and FunctionalDependencies,+-- though.++-- | As long as the type @n@ is an instance of Generic, you can create an instance+-- with just @instance Newtype n@+class Newtype n where+ type O n :: *+ type O n = GO (Rep n)++ pack :: O n -> n+ default pack :: (Generic n, GNewtype (Rep n), O n ~ GO (Rep n)) => O n -> n+ pack = to . gpack++ unpack :: n -> O n+ default unpack :: (Generic n, GNewtype (Rep n), O n ~ GO (Rep n)) => n -> O n+ unpack = gunpack . from++-- |+-- This function serves two purposes:+--+-- 1. Giving you the unpack of a newtype without you needing to remember the name.+--+-- 2. Showing that the first parameter is /completely ignored/ on the value level,+-- meaning the only reason you pass in the constructor is to provide type+-- information. Typeclasses sure are neat.+op :: (Newtype n,o ~ O n ) => (o -> n) -> n -> o+op _ = unpack++-- | The workhorse of the package. Given a pack and a \"higher order function\",+-- it handles the packing and unpacking, and just sends you back a regular old+-- function, with the type varying based on the hof you passed.+--+-- The reason for the signature of the hof is due to 'ala' not caring about structure.+-- To illustrate why this is important, another function in this package is 'under'.+-- It is not extremely useful; @under2@ might be more useful (with e.g., @mappend@),+-- but then we already digging the trench of+-- \"What about @under3@? @under4@?\".+-- The solution utilized here is to just hand off the \"packer\" to the hof.+-- That way your structure can be imposed in the hof,+-- whatever you may want it to be (e.g., List, Traversable).+ala :: (Newtype n, Newtype n', o' ~ O n', o ~ O n)+ => (o -> n) -> ((o -> n) -> b -> n') -> (b -> o')+ala pa hof = ala' pa hof id++-- | This is the original function seen in Conor McBride's work.+-- The way it differs from the 'ala' function in this package,+-- is that it provides an extra hook into the \"packer\" passed to the hof.+-- However, this normally ends up being @id@, so 'ala' wraps this function and+-- passes @id@ as the final parameter by default.+-- If you want the convenience of being able to hook right into the hof,+-- you may use this function.+ala' :: (Newtype n, Newtype n', o' ~ O n', o ~ O n)+ => (o -> n) -> ((a -> n) -> b -> n') -> (a -> o) -> (b -> o')+ala' _ hof f = unpack . hof (pack . f)++-- | A very simple operation involving running the function \'under\' the newtype.+-- Suffers from the problems mentioned in the 'ala' function's documentation.+under :: (Newtype n, Newtype n', o' ~ O n', o ~ O n)+ => (o -> n) -> (n -> n') -> (o -> o')+under _ f = unpack . f . pack++-- | The opposite of 'under'. I.e., take a function which works on the+-- underlying types, and switch it to a function that works on the newtypes.+over :: (Newtype n, Newtype n', o' ~ O n', o ~ O n)+ => (o -> n) -> (o -> o') -> (n -> n')+over _ f = pack . f . unpack++-- | 'under' lifted into a Functor.+underF :: (Newtype n, Newtype n', o' ~ O n', o ~ O n, Functor f)+ => (o -> n) -> (f n -> f n') -> (f o -> f o')+underF _ f = fmap unpack . f . fmap pack++-- | 'over' lifted into a Functor.+overF :: (Newtype n, Newtype n', o' ~ O n', o ~ O n, Functor f)+ => (o -> n) -> (f o -> f o') -> (f n -> f n')+overF _ f = fmap pack . f . fmap unpack++instance Newtype All where+ type O All = Bool+ pack = All+ unpack (All x) = x++instance Newtype Any where+ type O Any = Bool+ pack = Any+ unpack (Any x) = x++instance Newtype (Sum a) where+ type O (Sum a) = a+ pack = Sum+ unpack (Sum a) = a++instance Newtype (Product a) where+ type O (Product a) = a+ pack = Product+ unpack (Product a) = a++instance Newtype (First a) where+ type O (First a) = Maybe a+ pack = First+ unpack (First a) = a++instance Newtype (Last a) where+ type O (Last a) = Maybe a+ pack = Last+ unpack (Last a) = a++instance Newtype (Endo a) where+ type O (Endo a) = (a -> a)+ pack = Endo+ unpack (Endo a) = a++instance Newtype (ZipList a) where+ type O (ZipList a) = [a]+ pack = ZipList+ unpack (ZipList a) = a++instance Newtype (Const a x) where+ type O (Const a x) = a+ pack = Const+ unpack (Const a) = a++instance Newtype (Kleisli m a b) where+ type O (Kleisli m a b) = a -> m b+ pack = Kleisli+ unpack (Kleisli a) = a++instance Newtype (WrappedMonad m a) where+ type O (WrappedMonad m a) = m a+ pack = WrapMonad+ unpack (WrapMonad a) = a++instance Newtype (WrappedArrow a b c) where+ type O (WrappedArrow a b c) = a b c+ pack = WrapArrow+ unpack (WrapArrow a) = a++instance Newtype (ArrowMonad a b) where+ type O (ArrowMonad a b) = a () b+ pack = ArrowMonad+ unpack (ArrowMonad a) = a+++
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2011, Darius Jahandarie++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 Darius Jahandarie 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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ newtype-generics.cabal view
@@ -0,0 +1,40 @@+Name: newtype-generics+Version: 0.4+Synopsis: A typeclass and set of functions for working with newtypes, with generics support.+Description: Per Conor McBride, the Newtype typeclass represents the packing and unpacking of a newtype,+ and allows you to operatate under that newtype with functions such as ala.++ Generics support was added in version 0.4, making this package a full replacement+ for the original newtype package, and a better alternative to newtype-th.+License: BSD3+License-file: LICENSE+Author: Darius Jahandarie, Conor McBride, João Cristovao+Maintainer: João Cristóvão <jmacristovao@gmail.com>+-- Copyright: +Category: Control+Build-type: Simple+-- Extra-source-files: +Cabal-version: >=1.10++Library+ Exposed-modules: Control.Newtype+ Build-depends: base >= 3.0 && < 5+ -- Other-modules: + -- Build-tools: + Ghc-options: -Wall+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/jcristovao/newtype-generics++test-suite test+ type: exitcode-stdio-1.0+ main-is: main.hs+ hs-source-dirs: test,.+ build-depends: base >= 4.5 && < 5+ , hspec >= 1.7.2 && < 1.9+ , HUnit >= 1.2.5.2 && < 1.3+ default-language: Haskell2010++
+ test/main.hs view
@@ -0,0 +1,2 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}+