packages feed

flay 0.1 → 0.2

raw patch · 9 files changed

+325/−128 lines, 9 filesdep +ghc-primPVP ok

version bump matches the API change (PVP)

Dependencies added: ghc-prim

API changes (from Hackage documentation)

- Flay: instance (GHC.Base.Functor m, Flay.Flayable c m (f a) (g a) f g) => Flay.GFlay' c m (GHC.Generics.K1 r (f a)) (GHC.Generics.K1 r (g a)) f g
- Flay: instance c a => Flay.Flayable c m (f a) (g a) f g
- Flay: instance forall k (c :: k -> GHC.Types.Constraint) (m :: GHC.Types.* -> GHC.Types.*) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*). Flay.GFlay' c m GHC.Generics.V1 GHC.Generics.V1 f g
- Flay: instance forall k (m :: * -> *) (c :: k -> GHC.Types.Constraint) (s :: GHC.Types.* -> *) (t :: GHC.Types.* -> GHC.Types.*) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*) i (j :: GHC.Generics.Meta). (GHC.Base.Functor m, Flay.GFlay' c m s t f g) => Flay.GFlay' c m (GHC.Generics.M1 i j s) (GHC.Generics.M1 i j t) f g
- Flay: instance forall k (m :: * -> *) (c :: k -> GHC.Types.Constraint) (s :: GHC.Types.* -> *) (t :: GHC.Types.* -> GHC.Types.*) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*). (GHC.Base.Functor m, Flay.GFlay' c m s t f g) => Flay.GFlay' c m (GHC.Generics.Rec1 s) (GHC.Generics.Rec1 t) f g
- Flay: instance forall k (m :: * -> *) (c :: k -> GHC.Types.Constraint) (sl :: GHC.Types.* -> *) (tl :: GHC.Types.* -> GHC.Types.*) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*) (sr :: GHC.Types.* -> *) (tr :: GHC.Types.* -> GHC.Types.*). (GHC.Base.Applicative m, Flay.GFlay' c m sl tl f g, Flay.GFlay' c m sr tr f g) => Flay.GFlay' c m (sl GHC.Generics.:*: sr) (tl GHC.Generics.:*: tr) f g
- Flay: instance forall k (m :: * -> *) (c :: k -> GHC.Types.Constraint) (sl :: GHC.Types.* -> *) (tl :: GHC.Types.* -> GHC.Types.*) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*) (sr :: GHC.Types.* -> *) (tr :: GHC.Types.* -> GHC.Types.*). (GHC.Base.Functor m, Flay.GFlay' c m sl tl f g, Flay.GFlay' c m sr tr f g) => Flay.GFlay' c m (sl GHC.Generics.:+: sr) (tl GHC.Generics.:+: tr) f g
- Flay: instance forall k (m :: * -> *) (c :: k -> GHC.Types.Constraint) r x (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*). GHC.Base.Applicative m => Flay.GFlay' c m (GHC.Generics.K1 r x) (GHC.Generics.K1 r x) f g
- Flay: instance forall k s t (c :: k -> GHC.Types.Constraint) (m :: GHC.Types.* -> GHC.Types.*) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*). (GHC.Generics.Generic s, GHC.Generics.Generic t, Flay.GFlay' c m (GHC.Generics.Rep s) (GHC.Generics.Rep t) f g) => Flay.GFlay c m s t f g
- Flay: outer :: Monad m => Flay Trivial m (m x) (m x) m m -> m x -> m x
- Flay: type Flay (c :: k -> Constraint) (m :: * -> *) s t (f :: k -> *) (g :: k -> *) = (forall (a :: k). Dict (c a) -> f a -> m (g a)) -> s -> m t
+ Flay: class Flayable1 (c :: k -> Constraint) (r :: (k -> *) -> *) where flay1 = gflay
+ Flay: class GRecord (a :: * -> *)
+ Flay: class GTerminal (f :: * -> *)
+ Flay: class Record a
+ Flay: class Terminal a
+ Flay: collect1 :: (Monoid b, Flayable1 c r) => (forall a. Dict (c a) -> f a -> b) -> r f -> b
+ Flay: flay1 :: (Flayable1 c r, GFlay c (r f) (r g) f g) => Flay c (r f) (r g) f g
+ Flay: gterminal :: GTerminal f => f p
+ Flay: instance (Flay.GRecord l, Flay.GRecord r) => Flay.GRecord (l GHC.Generics.:*: r)
+ Flay: instance (Flay.GTerminal l, Flay.GTerminal r) => Flay.GTerminal (l GHC.Generics.:*: r)
+ Flay: instance (GHC.Generics.Generic a, Flay.GRecord (GHC.Generics.Rep a)) => Flay.Record a
+ Flay: instance (GHC.Generics.Generic a, Flay.GTerminal (GHC.Generics.Rep a)) => Flay.Terminal a
+ Flay: instance Flay.GRecord (GHC.Generics.K1 r x)
+ Flay: instance Flay.GRecord GHC.Generics.U1
+ Flay: instance Flay.GRecord x => Flay.GRecord (GHC.Generics.M1 i j x)
+ Flay: instance Flay.GTerminal f => Flay.GTerminal (GHC.Generics.M1 i c f)
+ Flay: instance Flay.Terminal ()
+ Flay: instance Flay.Terminal x => Flay.GTerminal (GHC.Generics.K1 i x)
+ Flay: instance forall k (a :: k). Flay.Terminal (Data.Functor.Const.Const () a)
+ Flay: instance forall k (c :: k -> GHC.Types.Constraint) (a :: k) r (f :: k -> *) (g :: k -> *). c a => Flay.GFlay' c (GHC.Generics.K1 r (f a)) (GHC.Generics.K1 r (g a)) f g
+ Flay: instance forall k (c :: k -> GHC.Types.Constraint) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*). Flay.GFlay' c GHC.Generics.V1 GHC.Generics.V1 f g
+ Flay: instance forall k (c :: k -> GHC.Types.Constraint) (s :: GHC.Types.* -> *) (t :: GHC.Types.* -> *) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*) i (j :: GHC.Generics.Meta). Flay.GFlay' c s t f g => Flay.GFlay' c (GHC.Generics.M1 i j s) (GHC.Generics.M1 i j t) f g
+ Flay: instance forall k (c :: k -> GHC.Types.Constraint) (sl :: GHC.Types.* -> *) (tl :: GHC.Types.* -> *) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*) (sr :: GHC.Types.* -> *) (tr :: GHC.Types.* -> *). (Flay.GFlay' c sl tl f g, Flay.GFlay' c sr tr f g) => Flay.GFlay' c (sl GHC.Generics.:*: sr) (tl GHC.Generics.:*: tr) f g
+ Flay: instance forall k (c :: k -> GHC.Types.Constraint) (sl :: GHC.Types.* -> *) (tl :: GHC.Types.* -> *) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*) (sr :: GHC.Types.* -> *) (tr :: GHC.Types.* -> *). (Flay.GFlay' c sl tl f g, Flay.GFlay' c sr tr f g) => Flay.GFlay' c (sl GHC.Generics.:+: sr) (tl GHC.Generics.:+: tr) f g
+ Flay: instance forall k s t (c :: k -> GHC.Types.Constraint) (f :: k -> GHC.Types.*) (g :: k -> GHC.Types.*). (GHC.Generics.Generic s, GHC.Generics.Generic t, Flay.GFlay' c (GHC.Generics.Rep s) (GHC.Generics.Rep t) f g) => Flay.GFlay c s t f g
+ Flay: terminal :: Terminal a => a
+ Flay: trivial1 :: (Applicative m, Flayable1 Trivial r) => (forall a. Trivial a => f a -> m (g a)) -> (r f) -> m (r g)
+ Flay: type Flay (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *) = forall m. Applicative m => (forall (a :: k). Dict (c a) -> f a -> m (g a)) -> s -> m t
+ Flay: unsafeZip :: forall c s0 s1 t0 t1 f g h. Record s0 => (Flay c s0 t0 f (Const ())) -> (Flay c s1 t1 g h) -> (forall x. Dict (c x) -> f x -> g x -> h x) -> s0 -> s1 -> t1
+ Flay: zip :: (Record s0, Flayable c s0 t0 f (Const ()), Flayable c s1 t1 g h) => (forall x. Dict (c x) -> f x -> g x -> h x) -> s0 -> s1 -> t1
+ Flay: zip1 :: (Record (s f), Flayable1 c s) => (forall x. Dict (c x) -> f x -> g x -> h x) -> s f -> s g -> s h
- Flay: class Flayable (c :: * -> Constraint) m s t f g | s -> f, t -> g, s g -> t, t f -> s where flay = gflay
+ Flay: class Flayable (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *) | s -> f, t -> g, s g -> t, t f -> s
- Flay: class (Generic s, Generic t, GFlay' c m (Rep s) (Rep t) f g) => GFlay (c :: k -> Constraint) m s t f g
+ Flay: class (Generic s, Generic t, GFlay' c (Rep s) (Rep t) f g) => GFlay (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *)
- Flay: class GFlay' (c :: k -> Constraint) m s t f g
+ Flay: class GFlay' (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *)
- Flay: collect :: (Monoid b, Flayable c (Const b) s t f (Const ())) => (forall a. Dict (c a) -> f a -> b) -> s -> b
+ Flay: collect :: (Monoid b, Flayable c s t f (Const ())) => (forall a. Dict (c a) -> f a -> b) -> s -> b
- Flay: collect' :: Monoid b => Flay c (Const b) s t f (Const ()) -> (forall a. Dict (c a) -> f a -> b) -> s -> b
+ Flay: collect' :: Monoid b => Flay c s t f (Const ()) -> (forall a. Dict (c a) -> f a -> b) -> s -> b
- Flay: flay :: (Flayable c m s t f g, Functor m, GFlay c m s t f g) => Flay c m s t f g
+ Flay: flay :: Flayable c s t f g => Flay c s t f g
- Flay: gflay :: (Functor m, GFlay c m s t f g) => Flay c m s t f g
+ Flay: gflay :: GFlay c s t f g => Flay (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *)
- Flay: gflay' :: GFlay' c m s t f g => Flay c m (s p) (t p) f g
+ Flay: gflay' :: GFlay' c s t f g => Flay c (s p) (t p) f g
- Flay: inner :: Flay Trivial Identity s s f f -> s -> s
+ Flay: inner :: Flay Trivial s s f f -> s -> s
- Flay: trivial :: Flayable Trivial m s t f g => (forall a. Trivial a => f a -> m (g a)) -> s -> m t
+ Flay: trivial :: (Applicative m, Flayable Trivial s t f g) => (forall a. Trivial a => f a -> m (g a)) -> s -> m t
- Flay: trivial' :: forall m s t f g. Flay Trivial m s t f g -> (forall a. Trivial a => f a -> m (g a)) -> s -> m t
+ Flay: trivial' :: forall m s t f g. Applicative m => Flay Trivial s t f g -> (forall a. Trivial a => f a -> m (g a)) -> s -> m t

Files

CHANGELOG.md view
@@ -1,3 +1,20 @@+# Version 0.2++* BREAKING CHANGE: The `m` parameter in in `Flay` and `Flayable` has been+  existentialized, to be chosen by the caller.++* BREAKING CHANGE: We don't use `DefaultSignatures` for `flay = gflay` anymore.+  This is very sad, but unfortunately type inferrence for the `c` type variable+  in new `Flayable` instances doesn't work.++* Added `Flayable1`, `trivial1`, `collect1`, `Terminal`, `GTerminal`, `zip1`,+  `zip`, `Record`, `GRecord`.++* Removed `outer`.++* Made compatible with GHC 8.2.2.++ # Version 0.1  * Initial version.
README.md view
@@ -1,12 +1,10 @@ # flay -[![Build Status](https://travis-ci.org/k0001/flay.svg?branch=master)](https://travis-ci.org/k0001/flay)- See the [BSD3 LICENSE](https://github.com/k0001/flay/blob/master/flay/LICENSE.txt) file to learn about the legal terms and conditions for this library.  Find documentation for this library in the-[`Flay`](https://github.com/k0001/flay/blob/master/flay/src/Flay.hs) module.+[`Flay`](https://github.com/k0001/flay/blob/master/src/Flay.hs) module.  You can build this library with [Nix](https://nixos.org/nix) by running `nix-build` on the top level of this module. `nix-shell` works as well.
default.nix view
@@ -1,1 +1,2 @@-(import ./release.nix {}).flay+{ compiler ? "ghc822" }:+(import ./release.nix {}).${compiler}.flay
flay.cabal view
@@ -1,17 +1,17 @@ name: flay-version: 0.1+version: 0.2 author: Renzo Carbonara maintainer: renλren!zone copyright: Renzo Carbonara 2017 license: BSD3 license-file: LICENSE.txt extra-source-files: README.md CHANGELOG.md pkg.nix shell.nix default.nix release.nix-category: Data+category: Data, Generics build-type: Simple cabal-version: >=1.18-synopsis: Work on your datatype without knowing its shape nor its contents.+synopsis: Work generically on your datatype without knowing its shape nor its contents. description:-  Work on your datatype without knowing its shape nor its contents using+  Work generically on your datatype without knowing its shape nor its contents using   a principlied approach. homepage: https://github.com/k0001/flay bug-reports: https://github.com/k0001/flay/issues@@ -27,7 +27,8 @@   exposed-modules: Flay   build-depends:     base >=4.9 && <5.0,-    constraints+    constraints,+    ghc-prim  test-suite tests   default-language: Haskell2010
pkg.nix view
@@ -1,12 +1,12 @@-{ mkDerivation, base, constraints, stdenv, tasty, tasty-quickcheck+{ mkDerivation, base, constraints, ghc-prim, stdenv, tasty, tasty-quickcheck }: mkDerivation {   pname = "flay";-  version = "0.1";+  version = "0.2";   src = ./.;-  libraryHaskellDepends = [ base constraints ];+  libraryHaskellDepends = [ base constraints ghc-prim ];   testHaskellDepends = [ base tasty tasty-quickcheck ];   homepage = "https://github.com/k0001/flay";-  description = "Work on your datatype without knowing much about it";+  description = "Work on your datatype without knowing its shape nor its contents";   license = stdenv.lib.licenses.bsd3; }
release.nix view
@@ -1,21 +1,30 @@ { nixpkgsBootstrap ? <nixpkgs> , nixpkgs ? (import nixpkgsBootstrap {}).fetchFromGitHub {     owner = "NixOS";-    repo = "nixpkgs";-    rev = "100919ab5b69cbbb26886421aacc692467c7fec4"; # release-17.03-    sha256 = "1nqiphqvxzszi0r4qq8w39x0g08wc7vaa9mfl7gi4a28bkv99781"; }+    repo = "nixpkgs-channels";+    rev = "3eccd0b11d176489d69c778f2fcb544438f3ab56"; # unstable, dec 4 2017+    sha256 = "1i3p5m0pnn86lzni5y1win0sacckw3wlg9kqaw15nszhykgz22zq"; } }:  let- pkgs = import nixpkgs {};  hsPackageSetConfig = self: super: {   flay = self.callPackage (import ./pkg.nix) {}; }; -ghc802 = pkgs.haskell.packages.ghc802.override {-  packageSetConfig = hsPackageSetConfig;+x = {+  ghc802 = pkgs.haskell.packages.ghc802.override { packageSetConfig = hsPackageSetConfig; };+  ghc822 = pkgs.haskell.packages.ghc822.override { packageSetConfig = hsPackageSetConfig; }; }; -in { inherit (ghc802) flay; }+in rec {+  ghc802 = { inherit (x.ghc802) flay; };+  ghc822 = { inherit (x.ghc822) flay; };++  everything = pkgs.releaseTools.aggregate {+    name = "everything";+    meta.description = "Every job in release.nix";+    constituents = [ ghc802.flay ghc822.flay ];+  };+}
shell.nix view
@@ -1,1 +1,2 @@-(import ./default.nix).env+{ compiler ? "ghc822" }:+(import ./release.nix {}).${compiler}.flay.env
src/Flay.hs view
@@ -1,4 +1,6 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleContexts #-}@@ -12,14 +14,11 @@ {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE UndecidableSuperClasses #-} --- {-# LANGUAGE LambdaCase #-}--- {-# LANGUAGE StandaloneDeriving #-}- -- | The most commonly used names in this module are intended to be imported -- unqualified: -- -- @--- import Flay (Flay, Flayable(flay), Dict(Dict))+-- import Flay (Flay, Flayable(flay), gflay, Dict(Dict)) -- @ -- -- The rest of the names, qualified:@@ -30,9 +29,9 @@ module Flay  ( Flay  , inner- , outer  -- * Flayable  , Flayable(flay)+ , Flayable1(flay1)  -- ** Generics  , gflay  , GFlay@@ -41,38 +40,53 @@  , All  , Trivial  , trivial+ , trivial1  , trivial'  , collect+ , collect1  , collect'+ , zip+ , zip1+ , unsafeZip+ , Record+ , GRecord+ , terminal+ , Terminal+ , GTerminal(gterminal)  -- ** Re-exports  , Dict(Dict)  ) where -import Control.Monad (join)-import Data.Functor.Identity (Identity, runIdentity)+import Control.Monad.ST (ST, runST) import Data.Functor.Const (Const(Const, getConst))+import Data.Functor.Identity (Identity(runIdentity)) import Data.Constraint (Constraint, Dict(Dict))+import Data.STRef (STRef, newSTRef, readSTRef, writeSTRef) import qualified GHC.Generics as G+import Prelude hiding (zip)+import Unsafe.Coerce (unsafeCoerce) +#if MIN_VERSION_ghc_prim(0,5,1)+import GHC.Types (Any)+#else+import GHC.Prim (Any)+#endif++ -------------------------------------------------------------------------------- --- | @'Flay' c m s t f g@ allows converting @s@ to @t@ by replacing+-- | @'Flay' c s t f g@ allows converting @s@ to @t@ by replacing -- ocurrences of @f@ with @g@ by applicatively applying a function -- @(forall a. c a => f a -> m (g a))@ to targeted occurences of @f a@ inside -- @s@. ----- A 'Flay' must obey the 'inner' identity law (and 'outer' identity law as--- well, if the 'Flay' fits the type expected by 'outer').------ When defining 'Flay' values, you should leave @c@, @m@, @f@, and @g@ fully--- polymomrphic, as these are the most useful types of 'Flay's.+-- A 'Flay' must obey the 'inner' identity law. ----- When using a 'Flay', @m@ will be required to be a 'Functor' in case the 'Flay'--- targets one element, or an 'Applicative' if it targets more than one. There--- will be no constraints on the rest of the arguments to 'Flay'.+-- When defining 'Flay' values, you should leave @c@, @f@, and @g@ fully+-- polymorphic, as these are the most useful types of 'Flay's. -- -- We use @'Dict' (c a) ->@ instead of @c a =>@ because the latter is often not--- enough to satisfy the type checker. With this approach, one must explicitely+-- enough to satisfy the type checker. With this approach, one must explicitly -- pattern match on the @'Dict' (c a)@ constructor in order to bring the @c a@ -- instance to scope.  Also, it's necessary that @c@ is explicitly given a type -- at the 'Flay''s call site, as otherwise the type checker won't be able to@@ -80,9 +94,9 @@ -- -- /to flay: tr. v., to strip off the skin or surface of./ ----- /Mnemonic for @c m s t f g@: CoMmon STandard FoG./+-- /Mnemonic for @c s t f g@: Common STandard FoG./ ----- ==== Example 1: Removing uncertaininy+-- ==== Example 1: Removing uncertainty -- -- Consider the following types and values: --@@ -93,7 +107,7 @@ -- @ -- -- @--- flayFoo :: ('Applicative' m, c 'Int', c 'Bool') => 'Flay' c m (Foo f) (Foo g) f g+-- flayFoo :: ('Applicative' m, c 'Int', c 'Bool') => 'Flay' c (Foo f) (Foo g) f g -- flayFoo h (Foo a b) = Foo \<$> h 'Dict' a \<*> h 'Dict' b -- @ --@@ -155,10 +169,10 @@ -- 'Just' (Foo ('Right' 2) ('Right' 'True')) -- @ ----- 'Flay', among other things, is intended to generalize this pattern so that+-- 'Flay', among other things, is intended to generalize this pattern, so that -- whatever choice of 'Foo', 'Maybe' or 'Identity' you make, you can use -- 'Applicative' this way. The easiest way to use 'Flay' is through 'trivial'',--- which is sufficient unless we need to enforce some constrain in the target+-- which is sufficient unless we need to enforce some constraint in the target -- elements wrapped in @m@ inside foo (we don't need this now). With 'trivial'', -- we could have defined @fooMToG@ this way: --@@ -182,7 +196,7 @@ -- flayMToG fl = 'trivial'' fl ('fmap' 'pure') -- @ ----- This is the escence of 'Flay': We can work operate on the contents of a+-- This is the esscence of 'Flay': We can work operating on the contents of a -- datatype @s@ targeted by a given 'Flay' /without/ knowing anything about @s@, -- nor about the @forall x. f x@ targets of the 'Flay'. And we do this using an -- principled approach relying on 'Applicative' and 'Functor'.@@ -201,7 +215,7 @@ -- -- ==== Example 2: Standalone @m@ ----- In the previous example, @flayFoo@ took the type @Flay 'Trivial' m (Foo m) (Foo+-- In the previous example, @flayFoo@ took the type @Flay 'Trivial' (Foo m) (Foo -- g) m g@ when it was used in @flayMToG@. That is, @m@ and @f@ were unified by -- our use of 'fmap'. However, keeping these different opens interesting -- possibilities. For example, let's try and convert a @Foo 'Maybe'@ to a @Foo@@ -269,7 +283,7 @@ -- Notice how we had to give an explicit type to our function @h@: This is -- because can't infer our @'Read' a@ constraint. You will always need to -- explicitly type the received @'Dict'@ unless the @c@ argument to 'Flay' has--- been explicitely by other means (like in the definition of 'trivial'', where+-- been explicitly by other means (like in the definition of 'trivial'', where -- we don't have to explicitly type 'Dict' because @c ~ 'Trivial'@ according to -- the top level signature of 'trivial''). --@@ -314,57 +328,69 @@ -- about the targets themselves beyond the fact that they satisfy a particular -- constraint. ---type Flay (c :: k -> Constraint) (m :: * -> *) s t (f :: k -> *) (g :: k -> *)-  = (forall (a :: k). Dict (c a) -> f a -> m (g a)) -> s -> m t+type Flay (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *)+  = forall m. Applicative m+       => (forall (a :: k). Dict (c a) -> f a -> m (g a)) -> s -> m t  -- | Inner identity law: -- -- @ -- (\\fl -> 'runIdentity' . 'trivial'' fl 'pure') = 'id' -- @-inner :: Flay Trivial Identity s s f f -> s -> s+inner :: Flay Trivial s s f f -> s -> s inner fl = runIdentity . trivial' fl pure --- | Outer identity law:------ @--- (\\fl -> 'join' . 'trivial'' fl 'pure') = 'id'--- @-outer :: Monad m => Flay Trivial m (m x) (m x) m m -> m x -> m x-outer fl = join . trivial' fl pure-- --------------------------------------------------------------------------------  -- | Default 'Flay' implementation for @s@ and @t@. ----- When defining 'Flayable' instances, you should leave @c@, @m@, @f@, and @g@+-- When defining 'Flayable' instances, you should leave @c@, @f@, and @g@ -- fully polymomrphic, as these are the most useful types of 'Flayables's.---- TODO: See if `c` can be made of kind `k -> Constraint`, probably in GHC 8.2.-class Flayable (c :: * -> Constraint) m s t f g | s -> f, t -> g, s g -> t, t f -> s where-  flay :: Flay c m s t f g-  -- | If @s@ and @g@ are instances of 'G.Generic', then 'flay' gets a default-  -- implementation. For example, provided the @Foo@ datatype shown in the-  -- documentation for 'Flay' had a 'G.Generic' instance, then the following-  -- 'Flayable' instance would get a default implementation for 'flay':+class Flayable (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *) | s -> f, t -> g, s g -> t, t f -> s where+  -- | If @s@ and @t@ are instances of 'G.Generic', then 'gflay' can be used as+  -- default implementation for 'flay'. For example, provided the @Foo@ datatype+  -- shown in the documentation for 'Flay' had a 'G.Generic' instance, then the+  -- following 'Flayable' instance would get a default implementation for+  -- 'flay':   --   -- @-  -- instance ('Applicative' m, c 'Int', c 'Bool') => 'Flayable' c m (Foo f) (Foo g) f g+  -- instance (c 'Int', c 'Bool') => 'Flayable' c (Foo f) (Foo g) f g where+  --   'flay' = 'gflay'   -- @   ---  -- Notice that while this default definition works for an @s@ having "nested-  -- 'Flayables'", GHC will prompt you for some additional constraints related-  -- to 'GFlay'' in order for it to compile.-  default flay :: (Functor m, GFlay c m s t f g) => Flay c m s t f g-  flay = gflay-  {-# INLINE flay #-}+  -- Notice that 'flay' can be defined in terms of 'flay1' as well.+  --+  -- /Implementors note:/ Unfortunately, due to some strange bug in GHC, we+  -- can't use @DefaultSignatures@ to say @'flay' = 'gflay'@, because when doing+  -- that the kind of @c@ infers incorrectly.+  flay :: Flay c s t f g --- | Isomorphic to @c a => f a -> m (g a)@.-instance {-# OVERLAPPABLE #-} c a => Flayable c m (f a) (g a) f g where-  flay = \h fa -> h Dict fa-  {-# INLINE flay #-}+-------------------------------------------------------------------------------- +-- | 'Flayable1' is 'Flayable' specialized for the common case of @s ~ r f@ and+-- @t ~ r g@. The rationale for introducing this seemingly redundant class is+-- that the 'Flayable1' constraint is less verbose than 'Flayable'.+--+-- Unfortunately, we can't readily existentialize the arguments to 'Flayable',+-- which is why you'll need to specify both 'Flayable1' and 'Flayable'+-- instances. Notice, however, that 'flay1' can be defined in terms of+-- 'flay' and vice-versa, so this should be very mechanical.+class Flayable1 (c :: k -> Constraint) (r :: (k -> *) -> *) where+  -- | If @r f@ and @r g@ are instances of 'G.Generic', then 'flay1' gets a+  -- default implementation. For example, provided the @Foo@ datatype shown in+  -- the documentation for 'Flay' had a 'G.Generic' instance, then the following+  -- 'Flayable' instance would get a default implementation for 'flay1':+  --+  -- @+  -- instance (c 'Int', c 'Bool') => 'Flayable1' c Foo+  -- @+  --+  -- Notice that 'flay1' can be defined in terms of 'flay' as well.+  flay1 :: Flay c (r f) (r g) f g+  default flay1 :: GFlay c (r f) (r g) f g => Flay c (r f) (r g) f g+  flay1 = gflay+  {-# INLINE flay1 #-}+ --------------------------------------------------------------------------------  -- | 'Constraint' trivially satisfied by every type.@@ -384,11 +410,12 @@ -- @ trivial'   :: forall m s t f g-  .  Flay Trivial m s t f g+  .  Applicative m+  => Flay Trivial s t f g   -> (forall a. Trivial a => f a -> m (g a))   -> s   -> m t  -- ^-trivial' fl = \h s -> fl (\Dict fa -> h fa) s+trivial' fl = \h -> \s -> fl (\Dict fa -> h fa) s {-# INLINE trivial' #-}  -- | Like 'trivial'', but works on a 'Flayable' instead of taking an explicit@@ -398,58 +425,63 @@ -- 'trivial' = 'trivial'' 'flay' -- @ trivial-  :: Flayable Trivial m s t f g+  :: (Applicative m, Flayable Trivial s t f g)   => (forall a. Trivial a => f a -> m (g a))   -> s   -> m t  -- ^ trivial = trivial' flay {-# INLINE trivial #-} +-- | Like 'trivial'', but works on a 'Flayable1' instead of taking an explicit+-- 'Flay'.+--+-- @+-- 'trivial' = 'trivial'' 'flay1'+-- @+trivial1+  :: (Applicative m, Flayable1 Trivial r)+  => (forall a. Trivial a => f a -> m (g a))+  -> (r f)+  -> m (r g)  -- ^+trivial1 = trivial' flay1+{-# INLINE trivial1 #-}+ --------------------------------------------------------------------------------  -- | Convenience 'Constraint' for satisfying basic 'GFlay'' needs for @s@ and @t@.-class (G.Generic s, G.Generic t, GFlay' c m (G.Rep s) (G.Rep t) f g)-  => GFlay (c :: k -> Constraint) m s t f g-instance (G.Generic s, G.Generic t, GFlay' c m (G.Rep s) (G.Rep t) f g)-  => GFlay (c :: k -> Constraint) m s t f g+class (G.Generic s, G.Generic t, GFlay' c (G.Rep s) (G.Rep t) f g)+  => GFlay (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *)+instance (G.Generic s, G.Generic t, GFlay' c (G.Rep s) (G.Rep t) f g)+  => GFlay (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *) -gflay :: (Functor m, GFlay c m s t f g) => Flay c m s t f g++gflay :: GFlay c s t f g => Flay (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *) gflay = \h s -> G.to <$> gflay' h (G.from s) {-# INLINE gflay #-} -class GFlay' (c :: k -> Constraint) m s t f g where-  gflay' :: Flay c m (s p) (t p) f g+class GFlay' (c :: k -> Constraint) s t (f :: k -> *) (g :: k -> *) where+  gflay' :: Flay c (s p) (t p) f g -instance GFlay' c m G.V1 G.V1 f g where+instance GFlay' c G.V1 G.V1 f g where   gflay' _ _ = undefined -- unreachable   {-# INLINE gflay' #-} --- Is this necessary?-instance (Functor m, GFlay' c m s t f g)-  => GFlay' c m (G.Rec1 s) (G.Rec1 t) f g where-  gflay' h (G.Rec1 sp) = G.Rec1 <$> gflay' h sp-  {-# INLINE gflay' #-}--instance (Functor m, Flayable c m (f a) (g a) f g) => GFlay' c m (G.K1 r (f a)) (G.K1 r (g a)) f g where-  gflay' h (G.K1 fa) = G.K1 <$> flay h fa-  {-# INLINE gflay' #-}--instance Applicative m => GFlay' c m (G.K1 r x) (G.K1 r x) f g where-  gflay' _ x = pure x+instance c a => GFlay' c (G.K1 r (f a)) (G.K1 r (g a)) f g where+  gflay' h (G.K1 fa) = G.K1 <$> h Dict fa   {-# INLINE gflay' #-} -instance (Functor m, GFlay' c m s t f g)-  => GFlay' c m (G.M1 i j s) (G.M1 i j t) f g where+instance (GFlay' c s t f g)+  => GFlay' c (G.M1 i j s) (G.M1 i j t) f g where   gflay' h (G.M1 sp) = G.M1 <$> gflay' h sp   {-# INLINE gflay' #-} -instance (Applicative m, GFlay' c m sl tl f g, GFlay' c m sr tr f g)-  => GFlay' c m (sl G.:*: sr) (tl G.:*: tr) f g where+instance (GFlay' c sl tl f g, GFlay' c sr tr f g)+  => GFlay' c (sl G.:*: sr) (tl G.:*: tr) f g where   gflay' h (slp G.:*: srp) = (G.:*:) <$> gflay' h slp <*> gflay' h srp   {-# INLINE gflay' #-} -instance (Functor m, GFlay' c m sl tl f g, GFlay' c m sr tr f g)-  => GFlay' c m (sl G.:+: sr) (tl G.:+: tr) f g where+instance (GFlay' c sl tl f g, GFlay' c sr tr f g)+  => GFlay' c (sl G.:+: sr) (tl G.:+: tr) f g where   gflay' h x = case x of     G.L1 slp -> G.L1 <$> gflay' h slp     G.R1 srp -> G.R1 <$> gflay' h srp@@ -470,21 +502,148 @@ -- @ collect'   :: Monoid b-  => Flay c (Const b) s t f (Const ())+  => Flay c s t f (Const ())   -> (forall a. Dict (c a) -> f a -> b)   -> s   -> b    -- ^-collect' fl k = \s -> getConst (fl (\d fa -> Const (k d fa)) s)+collect' fl = \k -> \s -> getConst (fl (\d fa -> Const (k d fa)) s) {-# INLINE collect' #-}  -- | Like 'collect'', but works on a 'Flayable' instead of an explicit 'Flay'. collect-  :: (Monoid b, Flayable c (Const b) s t f (Const ()))+  :: (Monoid b, Flayable c s t f (Const ()))   => (forall a. Dict (c a) -> f a -> b)   -> s   -> b    -- ^-collect k = collect' flay k+collect = collect' flay {-# INLINE collect #-}++-- | Like 'collect'', but works on a 'Flayable1' instead of an explicit 'Flay'.+collect1+  :: (Monoid b, Flayable1 c r)+  => (forall a. Dict (c a) -> f a -> b)+  -> r f+  -> b    -- ^+collect1 = collect' flay1+{-# INLINE collect1 #-}++--------------------------------------------------------------------------------++-- | Handwavy class of which only product or record types are supposed to be+-- instances.+class Record a+instance {-# OVERLAPPABLE #-} (G.Generic a, GRecord (G.Rep a)) => Record a++class GRecord (a :: * -> *) where+instance GRecord G.U1+instance GRecord (G.K1 r x)+instance GRecord x => GRecord (G.M1 i j x)+instance (GRecord l, GRecord r) => GRecord (l G.:*: r)++--------------------------------------------------------------------------------+++-- | Witness that @a@ is a terminal object. That is, that @a@ can always be+-- constructed out of thin air.+class Terminal a where+  terminal :: a+instance Terminal () where+  terminal = ()+  {-# INLINE terminal #-}+instance {-# OVERLAPPABLE #-} (G.Generic a, GTerminal (G.Rep a)) => Terminal a where+  terminal = G.to gterminal+  {-# INLINE terminal #-}+instance Terminal (Const () a) where+  terminal = Const ()+  {-# INLINE terminal #-}++class GTerminal (f :: * -> *) where+  gterminal :: f p+instance Terminal x => GTerminal (G.K1 i x) where+  gterminal = G.K1 terminal+  {-# INLINE gterminal #-}+instance GTerminal f => GTerminal (G.M1 i c f) where+  gterminal = G.M1 gterminal+  {-# INLINE gterminal #-}+instance (GTerminal l, GTerminal r) => GTerminal (l G.:*: r) where+  gterminal = gterminal G.:*: gterminal+  {-# INLINE gterminal #-}++--------------------------------------------------------------------------------++-- | Zip two 'Flayable1's together.+--+-- Example pairing two of the 'Foo' values seen elsewhere in this file.+--+-- @+-- > let foo1 = 'Foo' ('Data.Functor.Identity.Identity' 0) ('Data.Functor.Identity.Identity' 'False')+-- >   :: 'Foo' 'Data.Functor.Identity.Identity'+--+-- > let foo2 = 'Foo' ('Just' 1) 'Nothing'+-- >   :: 'Foo' 'Maybe'+--+-- > 'zip1' (\('Dict' :: 'Dict' ('Trivial' x)) a b -> 'Data.Functor.Product.Pair' a b) foo1 foo2+-- >   :: 'Foo' ('Data.Functor.Product.Product' 'Data.Functor.Identity.Identity' 'Maybe')+-- 'Foo' ('Data.Functor.Product.Pair' ('Data.Functor.Identity.Identity' 0) ('Just' 1)) ('Data.Functor.Product.Pair' ('Data.Functor.Identity.Identity' 'False') 'Nothing')+--+-- > 'zip1' (\('Dict' :: 'Dict' ('Show' x)) ('Data.Functor.Identity.Identity' a) yb -> case yb of+-- >           'Nothing' -> 'Const' ('show' a)+-- >           'Just' b  -> 'Const' ('show' (a, b)) )+-- >      foo1 foo2+-- >   :: Foo ('Const' 'String')+-- Foo ('Const' \"(0,1)\") ('Const' \"False\")+-- @+--+-- Note: 'zip1' is safer but less general than 'unsafeZip'.+zip1+  :: (Record (s f), Flayable1 c s)+  => (forall x. Dict (c x) -> f x -> g x -> h x)+  -> s f+  -> s g+  -> s h  -- ^+zip1 h = unsafeZip flay1 flay1 h+{-# INLINABLE zip1 #-}++-- | Zip two 'Flayable's together.+--+-- 'zip' is like 'zip1', but for 'Flayable's.+--+-- Note: 'zip' is safer but less general than 'unsafeZip'.+zip+  :: ( Record s0+     , Flayable c s0 t0 f (Const ())+     , Flayable c s1 t1 g h )+  => (forall x. Dict (c x) -> f x -> g x -> h x)+  -> s0+  -> s1+  -> t1   -- ^+zip h = unsafeZip flay flay h+{-# INLINABLE zip #-}++-- | Unsafe version of 'zip' that doesn't guarantee that the given 'Flay's+-- target the same values. 'zip' makes this function safe by simply using+-- 'flay' twice.+unsafeZip+  :: forall c s0 s1 t0 t1 f g h+  .  Record s0+  => (Flay c s0 t0 f (Const ()))+  -> (Flay c s1 t1 g h)+  -> (forall x. Dict (c x) -> f x -> g x -> h x)+  -> s0+  -> s1+  -> t1  -- ^+unsafeZip fl0 fl1 pair = \s0 s1 -> runST $ do+    r <- newSTRef (collect' fl0 f1 s0)+    fl1 (f2 r) s1+  where+    f1 :: Dict (c a) -> f a -> [Any]+    f1 = \Dict fa -> [unsafeCoerce fa :: Any]+    f2 :: STRef z [Any] -> Dict (c a) -> g a -> ST z (h a)+    f2 r = \Dict !ga -> do+       (x:xs) <- readSTRef r+       writeSTRef r xs+       let !fa = unsafeCoerce (x :: Any) :: f a+       pure $! pair Dict fa ga  -------------------------------------------------------------------------------- 
tests/Main.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE UndecidableInstances #-}@@ -12,6 +13,7 @@ import Control.Monad (join) import Data.Functor.Const (Const(Const)) import Data.Functor.Identity (Identity(Identity), runIdentity)+import GHC.Exts (Constraint) import GHC.Generics (Generic) import qualified GHC.Generics as G import qualified Test.Tasty as Tasty@@ -26,10 +28,11 @@ data Foo f = Foo (f Int) (f Bool)   deriving (Generic) -flayFoo :: (Applicative m, c Int, c Bool) => Flay c m (Foo f) (Foo g) f g+flayFoo :: (c Int, c Bool) => Flay c (Foo f) (Foo g) f g flayFoo h (Foo a b) = Foo <$> h Dict a <*> h Dict b -instance (Applicative m, c Int, c Bool) => Flayable c m (Foo f) (Foo g) f g+instance (c Int, c Bool) => Flayable c (Foo f) (Foo g) f g where flay = gflay+instance (c Int, c Bool) => Flayable1 c Foo  deriving instance (Eq (f Int), Eq (f Bool)) => Eq (Foo f) deriving instance (Show (f Int), Show (f Bool)) => Show (Foo f)@@ -37,15 +40,23 @@ instance (QC.Arbitrary (f Int), QC.Arbitrary (f Bool)) => QC.Arbitrary (Foo f) where   arbitrary = Foo <$> QC.arbitrary <*> QC.arbitrary +-- example values+foo1 :: Applicative m => Foo m+foo1 = Foo (pure 0) (pure False)++foo2 :: Applicative m => Foo m+foo2 = Foo (pure 1) (pure True)+ --------------------------------------------------------------------------------  data Bar f = Bar (f Int) Int   deriving (Generic) -flayBar :: (Applicative m, c Int) => Flay c m (Bar f) (Bar g) f g+flayBar :: c Int => Flay c (Bar f) (Bar g) f g flayBar h (Bar a b) = Bar <$> h Dict a <*> pure b -instance (Applicative m, c Int, c Bool) => Flayable c m (Bar f) (Bar g) f g+instance (c Int) => Flayable c (Bar f) (Bar g) f g where flay = flay1+instance (c Int) => Flayable1 c Bar where flay1 = flayBar  deriving instance Eq (f Int) => Eq (Bar f) deriving instance Show (f Int) => Show (Bar f)@@ -61,23 +72,20 @@   | Qux3 (Foo f)   deriving (Generic) -flayQux :: (Applicative m, c Int, c Bool) => Flay c m (Qux f) (Qux g) f g+flayQux :: (c Int, c Bool) => Flay c (Qux f) (Qux g) f g flayQux h (Qux1 a b) = Qux1 <$> h Dict a <*> pure b flayQux h (Qux2 a b) = Qux2 <$> h Dict a <*> h Dict b flayQux h (Qux3 a) = Qux3 <$> flayFoo h a --- TODO: See if there is a way of removing all these constraints.-instance-  ( GFlay' c m (G.K1 G.R (Foo f)) (G.K1 G.R (Foo g)) f g-  , Applicative m-  , c Int-  , c Bool-  ) => Flayable c m (Qux f) (Qux g) f g+instance (c Int , c Bool) => Flayable c (Qux f) (Qux g) f g where+  flay h (Qux1 fa b) = Qux1 <$> h Dict fa <*> pure b+  flay h (Qux2 fa fb) = Qux2 <$> h Dict fa <*> h Dict fb+  flay h (Qux3 foo) = Qux3 <$> flay h foo  deriving instance (Eq (f Int), Eq (Foo f)) => Eq (Qux f) deriving instance (Show (f Int), Show (Foo f)) => Show (Qux f) -instance (QC.Arbitrary (f Int), QC.Arbitrary (Foo f)) => QC.Arbitrary (Qux f) where+instance (QC.Arbitrary (f Int), QC.Arbitrary (f Bool)) => QC.Arbitrary (Qux f) where   arbitrary = QC.oneof [ Qux1 <$> QC.arbitrary <*> QC.arbitrary                        , Qux2 <$> QC.arbitrary <*> QC.arbitrary                        , Qux3 <$> QC.arbitrary ]@@ -101,24 +109,27 @@   , QC.testProperty "Flayable: Qux: inner identity law" $       QC.forAll QC.arbitrary $ \(qux :: Qux Maybe) ->          qux === inner flay qux-  , QC.testProperty "Flayable: outer identity law" $-      QC.forAll QC.arbitrary $ \(ia :: Identity Int) ->-         ia === outer flay ia   , QC.testProperty "collectShow: Foo: flayFoo" $       QC.forAll QC.arbitrary $ \foo@(Foo (Identity a) (Identity b)) ->          [show a, show b] === collectShow' flayFoo foo   , QC.testProperty "collectShow: Foo: flay" $       QC.forAll QC.arbitrary $ \foo@(Foo (Identity a) (Identity b)) ->          [show a, show b] === collectShow' flay foo+  , QC.testProperty "collectShow: Foo: flay1" $+      QC.forAll QC.arbitrary $ \foo@(Foo (Identity a) (Identity b)) ->+         [show a, show b] === collectShow' flay1 foo   , QC.testProperty "collectShow: Bar: flayBar" $       QC.forAll QC.arbitrary $ \bar@(Bar (Identity a) _) ->          [show a] === collectShow' flayBar bar   , QC.testProperty "collectShow: Bar: flay" $       QC.forAll QC.arbitrary $ \bar@(Bar (Identity a) _) ->          [show a] === collectShow' flay bar+  , QC.testProperty "collectShow: Bar: flay1" $+      QC.forAll QC.arbitrary $ \bar@(Bar (Identity a) _) ->+         [show a] === collectShow' flay bar   ]   collectShow'-  :: Flay Show (Const [String]) s t Identity (Const ()) -> s -> [String]+  :: Flay Show s t Identity (Const ()) -> s -> [String] collectShow' fl = collect' fl (\Dict (Identity a) -> [show a])