Possible to generically remove function types from datatype, to allow deriveJSON? - haskell

I have several datatypes representing the state of an application. In various places in the datatype, I have embedded functions or monadic actions, eg.
data Foo = Foo Int (ActionM String)
data Bar = Bar Foo (Maybe Bar) (ActionM ())
I need to encode most of these datatypes as json so I can send it to the browser for display. Using deriveJSON (from the Aeson package) doesn't work because instances for ActionM can't be derived. However, I don't actually want those bits to be sent anyway. I currently have an approach which works but is basically copy-pasting the full set of datatypes and manually removing the embeded ActionM fields.
I (think I) need one of a couple of things. Either
a way to tell deriveJSON to just ignore fields that it can't figure out, and maybe parse them back into undefined. As far as I can tell this doesn't exist
a way to automatically generate a parallel set of datatypes with these fields removed. So I want to write something like
applyMagic Bar
and get back
data Foo' = Foo' Int
data Bar' = Bar' Foo' (Maybe Bar')
Is any of this possible, and how would I do it?

This is a simplistic solution, but couldn't you do something like
data Foo' = Foo' Int
type Foo = (ActionM String, Foo')
and simply obtain the second element of the tuple when you want to serialize?
Tuples are an instance of ComonadEnv, so you could also use functions like ask and extract.
Edit. Bar is a more complicated case because it is a recursive type. But it could be handled using the CofreeT comonad transformer:
import Data.Functor.Identity
import Data.Bifunctor (second)
import Control.Comonad -- from 'comonad'
import Control.Comonad.Hoist.Class
import Control.Comonad.Trans.Cofree -- from 'free'
-- Orphan ComonadHoist instance that will likely be added in future
-- versions of free
instance Functor f => ComonadHoist (CofreeT f) where
cohoist g = CofreeT . fmap (second (cohoist g)) . g . runCofreeT
type Bar = CofreeT Maybe ((,) (ActionM ())) Foo
type Bar' = Cofree Maybe Foo'
applyMagic :: Bar -> Bar'
applyMagic = cohoist (Identity . extract) . fmap extract
CofreeT Maybe ((,) (ActionM ())) Foo is a non-empty list of Foo values that have been annotated with ActionM () values.
Cofree Maybe Foo' is a non-empty list of Foo' values, without extra annotations (Cofree Maybe Foo is a synonym for CofreeT Maybe Identity Foo', where Identity works as the trivial comonad.).
To transform one into the other, applyMagic first uses fmap extract to transform all the Foos into Foo's, and then uses cohoist from ComonadHoist to remove the "annotation layer" underneath CofreeT.
In general, values with "extra context" can often be modeled with comonads.

Related

Type synonyms with TemplateHaskell

If I have a type data Foo = Foo Int Int where frequently (but not always) the second parameter is a (fixed) function of the first, I could write a helper function mkFoo m = Foo m (f m) to reduce duplication.
I have this exact problem, but at the type level. The natural solution might be to use singletons to promote f, but my f isn't easily promoted. Instead, I'm trying to use TemplateHaskell and reflection to evaluate f at compile time at the data level. For example, I can currently do this (using ‑XDataKinds and GHC.TypeLits):
f :: Integer -> Integer
data Bar (a::Nat) (b::Nat)
mkNat :: Integer -> Q Type -- constructs a TypeLit
bar :: Bar 5 $(mkNat $ f $ proxy natValue (Proxy::Proxy 5))
It's obviously annoying to have to write this with a concrete type every time I want to use this pattern. Unfortunately, I know of no shorter or generic way to write the signature for bar. In particular, I can't define the type synonym
type Bar' (m :: Nat) = Bar m $(mkNat $ f $ proxy natVal (Proxy::Proxy m))
bar :: Bar' 5
due to TH stage restrictions (m is not imported or known when compiling the synonym).
Is there any way to simplify the signature of bar?

Parsing to Free Monads

Say I have the following free monad:
data ExampleF a
= Foo Int a
| Bar String (Int -> a)
deriving Functor
type Example = Free ExampleF -- this is the free monad want to discuss
I know how I can work with this monad, eg. I could write some nice helpers:
foo :: Int -> Example ()
foo i = liftF $ Foo i ()
bar :: String -> Example Int
bar s = liftF $ Bar s id
So I can write programs in haskell like:
fooThenBar :: Example Int
fooThenBar =
do
foo 10
bar "nice"
I know how to print it, interpret it, etc. But what about parsing it?
Would it be possible to write a parser that could parse arbitrary
programs like:
foo 12
bar nice
foo 11
foo 42
So I can store them, serialize them, use them in cli programs etc.
The problem I keep running into is that the type of the program depends on which program is being parsed. If the program ends with a foo it's of
type Example () if it ends with a bar it's of type Example Int.
I do not feel like writing parsers for every possible permutation (it's simple here because there are only two possibilities, but imagine we add
Baz Int (String -> a), Doo (Int -> a), Moz Int a, Foz String a, .... This get's tedious and error-prone).
Perhaps I'm solving the wrong problem?
Boilerplate
To run the above examples, you need to add this to the beginning of the file:
{-# LANGUAGE DeriveFunctor #-}
import Control.Monad.Free
import Text.ParserCombinators.Parsec
Note: I put up a gist containing this code.
Not every Example value can be represented on the page without reimplementing some portion of Haskell. For example, return putStrLn has a type of Example (String -> IO ()), but I don't think it makes sense to attempt to parse that sort of Example value out of a file.
So let's restrict ourselves to parsing the examples you've given, which consist only of calls to foo and bar sequenced with >> (that is, no variable bindings and no arbitrary computations)*. The Backus-Naur form for our grammar looks approximately like this:
<program> ::= "" | <expr> "\n" <program>
<expr> ::= "foo " <integer> | "bar " <string>
It's straightforward enough to parse our two types of expression...
type Parser = Parsec String ()
int :: Parser Int
int = fmap read (many1 digit)
parseFoo :: Parser (Example ())
parseFoo = string "foo " *> fmap foo int
parseBar :: Parser (Example Int)
parseBar = string "bar " *> fmap bar (many1 alphaNum)
... but how can we give a type to the composition of these two parsers?
parseExpr :: Parser (Example ???)
parseExpr = parseFoo <|> parseBar
parseFoo and parseBar have different types, so we can't compose them with <|> :: Alternative f => f a -> f a -> f a. Moreover, there's no way to know ahead of time which type the program we're given will be: as you point out, the type of the parsed program depends on the value of the input string. "Types depending on values" is called dependent types; Haskell doesn't feature a proper dependent type system, but it comes close enough for us to have a stab at making this example work.
Let's start by forcing the expressions on either side of <|> to have the same type. This involves erasing Example's type parameter using existential quantification.†
data Ex a = forall i. Wrap (a i)
parseExpr :: Parser (Ex Example)
parseExpr = fmap Wrap parseFoo <|> fmap Wrap parseBar
This typechecks, but the parser now returns an Example containing a value of an unknown type. A value of unknown type is of course useless - but we do know something about Example's parameter: it must be either () or Int because those are the return types of parseFoo and parseBar. Programming is about getting knowledge out of your brain and onto the page, so we're going to wrap up the Example value with a bit of GADT evidence which, when unwrapped, will tell you whether a was Int or ().
data Ty a where
IntTy :: Ty Int
UnitTy :: Ty ()
data (a :*: b) i = a i :&: b i
type Sig a b = Ex (a :*: b)
pattern Sig x y = Wrap (x :&: y)
parseExpr :: Parser (Sig Ty Example)
parseExpr = fmap (\x -> Sig UnitTy x) parseFoo <|>
fmap (\x -> Sig IntTy x) parseBar
Ty is (something like) a runtime "singleton" representative of Example's type parameter. When you pattern match on IntTy, you learn that a ~ Int; when you pattern match on UnitTy you learn that a ~ (). (Information can be made to flow the other way, from types to values, using classes.) :*:, the functor product, pairs up two type constructors ensuring that their parameters are equal; thus, pattern matching on the Ty tells you about its accompanying Example.
Sig is therefore called a dependent pair or sigma type - the type of the second component of the pair depends on the value of the first. This is a common technique: when you erase a type parameter by existential quantification, it usually pays to make it recoverable by bundling up a runtime representative of that parameter.
Note that this use of Sig is equivalent to Either (Example Int) (Example ()) - a sigma type is a sum, after all - but this version scales better when you're summing over a large (or possibly infinite) set.
Now it's easy to build our expression parser into a program parser. We just have to repeatedly apply the expression parser, and then manipulate the dependent pairs in the list.
parseProgram :: Parser (Sig Ty Example)
parseProgram = fmap (foldr1 combine) $ parseExpr `sepBy1` (char '\n')
where combine (Sig _ val) (Sig ty acc) = Sig ty (val >> acc)
The code I've shown you is not exemplary. It doesn't separate the concerns of parsing and typechecking. In production code I would modularise this design by first parsing the data into an untyped syntax tree - a separate data type which doesn't enforce the typing invariant - then transform that into a typed version by type-checking it. The dependent pair technique would still be necessary to give a type to the output of the type-checker, but it wouldn't be tangled up in the parser.
*If binding is not a requirement, have you thought about using a free applicative to represent your data?
†Ex and :*: are reusable bits of machinery which I lifted from the Hasochism paper
So, I worry that this is the same sort of premature abstraction that you see in object-oriented languages, getting in the way of things. For example, I am not 100% sure that you are using the structure of the free monad -- your helpers for example simply seem to use id and () in a rather boring way, in fact I'm not sure if your Int -> x is ever anything other than either Pure :: Int -> Free ExampleF Int or const (something :: Free ExampleF Int).
The free monad for a functor F can basically be described as a tree whose data is stored in leaves and whose branching factor is controlled by the recursion in each constructor of the functor F. So for example Free Identity has no branching, hence only one leaf, and thus has the same structure as the monad:
data MonoidalFree m x = MF m x deriving (Functor)
instance Monoid m => Monad (MonoidalFree m) where
return x = MF mempty x
MF m x >>= my_x = case my_x x of MF n y -> MF (mappend m n) y
In fact Free Identity is isomorphic to MonoidalFree (Sum Integer), the difference is just that instead of MF (Sum 3) "Hello" you see Free . Identity . Free . Identity . Free . Identity $ Pure "Hello" as the means of tracking this integer. On the other hand if you have data E x = L x | R x deriving (Functor) then you get a sort of "path" of Ls and Rs before you hit this one leaf, Free E is going to be isomorphic to MonoidalFree [Bool].
The reason I'm going through this is that when you combine Free with an Integer -> x functor, you get an infinitely branching tree, and when I'm looking through your code to figure out how you're actually using this tree, all I see is that you use the id function with it. As far as I can tell, that restricts the recursion to either have the form Free (Bar "string" Pure) or else Free (Bar "string" (const subExpression)), in which case the system would seem to reduce completely to the MonoidalFree [Either Int String] monad.
(At this point I should pause to ask: Is that correct as far as you know? Was this what was intended?)
Anyway. Aside from my problems with your premature abstraction, the specific problem that you're citing with your monad (you can't tell the difference between () and Int has a bunch of really complicated solutions, but one really easy one. The really easy solution is to yield a value of type Example (Either () Int) and if you have a () you can fmap Left onto it and if you have an Int you can fmap Right onto it.
Without a much better understanding of how you're using this thing over TCP/IP we can't recommend a better structure for you than the generic free monads that you seem to be finding -- in particular we'd need to know how you're planning on using the infinite-branching of Int -> x options in practice.

Does exporting type constructors make a difference?

Let's say I have an internal data type, T a, that is used in the signature of exported functions:
module A (f, g) where
newtype T a = MkT { unT :: (Int, a) }
deriving (Functor, Show, Read) -- for internal use
f :: a -> IO (T a)
f a = fmap (\i -> T (i, a)) randomIO
g :: T a -> a
g = snd . unT
What is the effect of not exporting the type constructor T? Does it prevent consumers from meddling with values of type T a? In other words, is there a difference between the export list (f, g) and (f, g, T()) here?
Prevented
The first thing a consumer will see is that the type doesn't appear in Haddock documentation. In the documentation for f and g, the type Twill not be hyperlinked like an exported type. This may prevent a casual reader from discovering T's class instances.
More importantly, a consumer cannot doing anything with T at the type level. Anything that requires writing a type will be impossible. For instance, a consumer cannot write new class instances involving T, or include T in a type family. (I don't think there's a way around this...)
At the value level, however, the main limitation is that a consumer cannot write a type annotation including T:
> :t (f . read) :: Read b => String -> IO (A.T b)
<interactive>:1:39: Not in scope: type constructor or class `A.T'
Not prevented
The restriction on type signatures is not as significant a limitation as it appears. The compiler can still infer such a type:
> :t f . read
f . read :: Read b => String -> IO (A.T b)
Any value expression within the inferrable subset of Haskell may therefore be expressed regardless of the availability of the type constructor T. If, like me, you're addicted to ScopedTypeVariables and extensive annotations, you may be a little surprised by the definition of unT' below.
Furthermore, because typeclass instances have global scope, a consumer can use any available class functions without additional limitation. Depending on the classes involved, this may allow significant manipulation of values of the unexposed type. With classes like Functor, a consumer can also freely manipulate type parameters, because there's an available function of type T a -> T b.
In the example of T, deriving Show of course exposes the "internal" Int, and gives a consumer enough information to hackishly implement unT:
-- :: (Show a, Read a) => T a -> (Int, a)
unT' = (read . strip . show') `asTypeOf` (mkPair . g)
where
strip = reverse . drop 1 . reverse . drop 9
-- :: T a -> String
show' = show `asTypeOf` (mkString . g)
mkPair :: t -> (Int, t)
mkPair = undefined
mkString :: t -> String
mkString = undefined
> :t unT'
unT' :: (Show b, Read b) => A.T b -> (Int, b)
> x <- f "x"
> unT' x
(-29353, "x")
Implementing mkT' with the Read instance is left as an exercise.
Deriving something like Generic will completely explode any idea of containment, but you'd probably expect that.
Prevented?
In the corners of Haskell where type signatures are necessary or where asTypeOf-style tricks don't work, I guess not exporting the type constructor could actually prevent a consumer from doing something they could with the export list (f, g, T()).
Recommendation
Export all type constructors that are used in the type of any value you export. Here, go ahead and include T() in your export list. Leaving it out doesn't accomplish anything other than muddying the documentation. If you want to expose an purely abstract immutable type, use a newtype with a hidden constructor and no class instances.

What's the difference between makeLenses and makeFields?

Pretty self-explanatory. I know that makeClassy should create typeclasses, but I see no difference between the two.
PS. Bonus points for explaining the default behaviour of both.
Note: This answer is based on lens 4.4 or newer. There were some changes to the TH in that version, so I don't know how much of it applies to older versions of lens.
Organization of the lens TH functions
The lens TH functions are all based on one function, makeLensesWith (also named makeFieldOptics inside lens). This function takes a LensRules argument, which describes exactly what is generated and how.
So to compare makeLenses and makeFields, we only need to compare the LensRules that they use. You can find them by looking at the source:
makeLenses
lensRules :: LensRules
lensRules = LensRules
{ _simpleLenses = False
, _generateSigs = True
, _generateClasses = False
, _allowIsos = True
, _classyLenses = const Nothing
, _fieldToDef = \_ n ->
case nameBase n of
'_':x:xs -> [TopName (mkName (toLower x:xs))]
_ -> []
}
makeFields
defaultFieldRules :: LensRules
defaultFieldRules = LensRules
{ _simpleLenses = True
, _generateSigs = True
, _generateClasses = True -- classes will still be skipped if they already exist
, _allowIsos = False -- generating Isos would hinder field class reuse
, _classyLenses = const Nothing
, _fieldToDef = camelCaseNamer
}
What do these mean?
Now we know that the differences are in the simpleLenses, generateClasses, allowIsos and fieldToDef options. But what do those options actually mean?
makeFields will never generate type-changing optics. This is controlled by the simpleLenses = True option. That option doesn't have haddocks in the current version of lens. However, lens HEAD added documentation for it:
-- | Generate "simple" optics even when type-changing optics are possible.
-- (e.g. 'Lens'' instead of 'Lens')
So makeFields will never generate type-changing optics, while makeLenses will if possible.
makeFields will generate classes for the fields. So for each field foo, we have a class:
class HasFoo t where
foo :: Lens' t <Type of foo field>
This is controlled by the generateClasses option.
makeFields will never generate Iso's, even if that would be possible (controlled by the allowIsos option, which doesn't seem to be exported from Control.Lens.TH)
While makeLenses simply generates a top-level lens for each field that starts with an underscore (lowercasing the first letter after the underscore), makeFields will instead generate instances for the HasFoo classes. It also uses a different naming scheme, explained in a comment in the source code:
-- | Field rules for fields in the form # prefixFieldname or _prefixFieldname #
-- If you want all fields to be lensed, then there is no reason to use an #_# before the prefix.
-- If any of the record fields leads with an #_# then it is assume a field without an #_# should not have a lens created.
camelCaseFields :: LensRules
camelCaseFields = defaultFieldRules
So makeFields also expect that all fields are not just prefixed with an underscore, but also include the data type name as a prefix (as in data Foo = { _fooBar :: Int, _fooBaz :: Bool }). If you want to generate lenses for all fields, you can leave out the underscore.
This is all controlled by the _fieldToDef (exported as lensField by Control.Lens.TH).
As you can see, the Control.Lens.TH module is very flexible. Using makeLensesWith, you can create your very own LensRules if you need a pattern not covered by the standard functions.
Disclaimer: this is based on experimenting with the working code; it gave me enough information to proceed with my project, but I'd still prefer a better-documented answer.
data Stuff = Stuff {
_foo
_FooBar
_stuffBaz
}
makeLenses
Will create foo as a lens accessor to Stuff
Will create fooBar (changing the capitalized name to lowercase);
makeFields
Will create baz and a class HasBaz; it will make Stuff an instance of that class.
Normal
makeLenses creates a single top-level optic for each field in the type. It looks for fields that start with an underscore (_) and it creates an optic that is as general as possible for that field.
If your type has one constructor and one field you'll get an Iso.
If your type has one constructor and multiple fields you'll get many Lens.
If your type has multiple constructors you'll get many Traversal.
Classy
makeClassy creates a single class containing all the optics for your type. This version is used to make it easy to embed your type in another larger type achieving a kind of subtyping. Lens and Traversal optics will be created according to the rules above (Iso is excluded because it hinders the subtyping behavior.)
In addition to one method in the class per field you'll get an extra method that makes it easy to derive instances of this class for other types. All of the other methods have default instances in terms of the top-level method.
data T = MkT { _field1 :: Int, _field2 :: Char }
class HasT a where
t :: Lens' a T
field1 :: Lens' a Int
field2 :: Lens' a Char
field1 = t . field1
field2 = t . field2
instance HasT T where
t = id
field1 f (MkT x y) = fmap (\x' -> MkT x' y) (f x)
field2 f (MkT x y) = fmap (\y' -> MkT x y') (f y)
data U = MkU { _subt :: T, _field3 :: Bool }
instance HasT U where
t f (MkU x y) = fmap (\x' -> MkU x' y) (f x)
-- field1 and field2 automatically defined
This has the additional benefit that it is easy to export/import all the lenses for a given type. import Module (HasT(..))
Fields
makeFields creates a single class per field which is intended to be reused between all types that have a field with the given name. This is more of a solution to record field names not being able to be shared between types.

Functors don’t work with data types that require specific types

This works fine:
data Foo a = Foo a
instance Functor Foo where
fmap f (Foo s) = Foo (f s)
This throws an Error:
data Foo = Foo String
instance Functor Foo where
fmap f (Foo s) = Foo (f s)
Error:
Kind mis-match
The first argument of `Functor' should have kind `* -> *',
but `Foo' has kind `*'
In the instance declaration for `Functor Foo'
What am I missing here? Why can’t I use functors to wrap and unwrap Foo if it holds a specific type?
UPDATE
I guess I can ask this another way:
data Foo = Foo String deriving(Show)
let jack = Foo "Jack"
-- Some functory thingy here
putStrLn $ show $ tail <$> jack
-- Foo "ack"
Why can't I do this? Or is there another construct for this use case?
That's because Foo needs a single type variable to operate on.
The type of fmap is:
fmap :: Functor f => (a -> b) -> f a -> f b
Now try to specialize this for your Foo:
(a -> b) -> Foo -> Foo
Can you see where the problem is ? The types won't just match. So, to make Foo a functor, it has to be something like this:
Foo a
so that when you specialize it for fmap it has the following proper type:
(a -> b) -> Foo a -> Foo b
The type of fmap is generic; you can't constrain it:
fmap :: Functor f => (a -> b) -> f a -> f b
Those as and bs must be completely polymorphic (within the constraints of your Functor instance), or you don't have a Functor. The handwavy way of explaining why this is is because a Functor must obey some theoretical laws to make them play nice with Haskell's other data types:
fmap id = id
fmap (p . q) = (fmap p) . (fmap q)
If you have a data type that is parameterized over multiple types, i.e:
data Bar a b = Bar a b
You can write a Functor instance for Bar a:
instance Functor (Bar a) where
fmap f (Bar a b) = Bar a (f b)
You can also write a Bifunctor instance for Bar:
instance Bifunctor Foo where
first f (Bar a b) = Bar (f a) b
second f (Bar a b) = Bar a (f b)
...which again must follow some laws (listed on the linked page).
Edit:
You could write your own class to handle the type of behavior you're looking for, but it would look like this:
class FooFunctor f where
ffmap :: (String -> String) -> f -> f
But in this case, we'd have to make new entire classes for every single permutation of "inner types" we might have (like String), in order to cover all bases.
You can also write a class (call it Endo) that only permits endomorphisms (functions of type a -> a) on the "inner type" of a data type, like this:
class Endo f where
emap :: (a -> a) -> f a -> f a
Then, if you changed your data type a bit, and instantiated an appropriate instance of Endo, e.g.
data Foo' a = Foo' a
type Foo = Foo' String
instance Endo Foo' where
emap f (Foo a) = Foo (f a)
...if you write functions of type Foo -> Foo, you're guaranteed to preserve the "Stringiness" of the inner type you're mapping if you use emap. A quick search on hayoo reveals that this type of thing is relatively common practice, but doesn't really exist as a standard type class.
Coming from a dynamic language you probably see a Functor as a container of stuff and fmap as a way to transform things inside the container. However in Category theory a Functor can be seen as a way to transform a type into another type, and a way to transform a function on those type to function on the other type.
Imagine you have 2 differents worlds, one which is earth, and a virtual one when every body/things on earth has an avatar. The Functor is not the avatar but the magic wand which transforms everything to its avatar but also every single function of the real world into a function in the avatar world.
For example, with my magic wand I can transform a human to a frog (or a String to a list of Strings) but I can also transform the function "change the human hat" to change the frog hat" (or capitaize a String to capitalize all the String within a list).
fmap is the way you transform a function to another : you can see it as
a function which take 2 arguments - a function and a container - and apply this function to each element of this container
but also as a function which take 1 argmunt - a function - an return a function which takes a container and return a container.
The way you create a type from a type is less obvious In your first example you probably just see Foo String as new type, but you can also see Foo as a super function whic take the type String and return a new type : Foo String. That's what the * -> * kind is. Foo is not a type but a super function creating type from a type.
In your second example, Foo is not a type creator but just a simple type (kind : *), therefore it doesn't make sense to declare it as a functor.
If you really want to define fmap for the plain Foo in your 2nd example is to define a real functor and create a type alias for the plain type
data FooFunctor a = FooFunctor a
instance Functor Foofunctor where
fmap f (FooFunctor a) = FooFunctor (f a)
type Foo = FooFunctor String
A class that does pretty much exactly what you're asking for is MonoFunctor.
type instance Element Foo = String
instance MonoFunctor Foo where
fmap f (Foo s) = Foo (f s)
head "Jack" is not the string "J", but the character 'J'. So your own example shows why this does not work; head <$> jack would have to give Foo 'J', which is not a valid value of type Foo, since Foo can only be applied to String values, not Char values.
"Some other construct" for this use case is to define a "map" function for Foo, exactly as you're trying to define fmap. But that map function is not fmap, since it has to have type (String -> String) -> Foo -> Foo. So there's no need (or possibility) to make Foo an instance of Functor and name your mapping function fmap; the mapping function you want to use is simply not fmap.
Note that this means you cannot map arbitrary functions over your Foo values; only functions which take and return strings (so head is still out). Nor can you pass Foo values to generic functions that accept values in any functor; those functions might try to fmap functions over Foo that do not return strings; they're allowed to do this because they specified that they need functors, and that's exactly what defines a functor.

Resources