I'm trying to create a custom data type. As an example
data Time = Second Int deriving (Show)
However, this is too limiting (we could say later need milliseconds). I would like to instead define something like this:
data Time = Second Num deriving (Show)
This doesn't compile because Num has kind * -> ghc-prim-0.4.0.0:GHC.Prim.Constraint
How do I setup Time such that Second may contain any Num?
One of the best examples of why this might not be so desirable is found here at the Wikibooks section on Classes and Types. They say:
Type constraints in data declarations are less useful than it might seem at first. Consider:
data (Num a) => Foo a = F1 a | F2 a String
Here, Foo is a type with two constructors, both taking an argument of a type a which must be in Num. However, the (Num a) => constraint is only effective for the F1 and F2 constructors, and not for other functions involving Foo. Therefore, in the following example...
fooSquared :: (Num a) => Foo a -> Foo a
fooSquared (F1 x) = F1 (x * x)
fooSquared (F2 x s) = F2 (x * x) s
... even though the constructors ensure a will be some type in Num we can't avoid duplicating the constraint in the signature of fooSquared
This suggests that a reasonable option for you is to just create Time with a generic parameter, and then later ensure that the module functions that operate on Time data always have the necessary constraint for Num.
It won't be so much of a worry that someone goes off and foolishly makes Time String or something -- if they do, then none of the provided module functions are going to be helpful for them, so it doesn't matter so much.
There are also options to look up with GADTs, the {-# LANGUAGE GeneralizedNewtypeDeriving #-} pragma, and the {-# LANGUAGE DatatypeContexts #-} pragma. But usually these start to rope in unnecessary degrees of extra complexity, especially if you're a Haskell novice like me.
There is a deprecated feature called Datatype Contexts that allows you to do that:
{-# LANGUAGE DatatypeContexts #-}
data Num a => Time a = Second a deriving (Show)
t = Second (5 :: Int)
main = print t
This executes on GHC 7.8.3 (sorry, don't have 7.10 to check), but warns you about the deprecation:
t.hs:1:14: Warning:
-XDatatypeContexts is deprecated: It was widely considered a
misfeature, and has been removed from the Haskell language.
Second 5
A non-deprecated way to do it is to use Generalized Algebraic Datatypes (GADTs) (and you'll need standalone deriving as well):
{-# LANGUAGE GADTs, StandaloneDeriving #-}
data Time a where
Second :: Num a => a -> Time a
deriving instance Show a => Show (Time a)
t = Second (5 :: Int)
main = print t
If you try to create a variable with something non-Num, you'll get a compilation error:
t = Second "a"
t.hs:12:5:
No instance for (Num [Char]) arising from a use of ‘Second’
In the expression: Second "a"
In an equation for ‘t’: t = Second "a"
Related
I am using data-reify and graphviz to transform an eDSL into a nice graphical representation, for introspection purposes.
As simple, contrived example, consider:
{-# LANGUAGE GADTs #-}
data Expr a where
Constant :: a -> Expr a
Map :: (other -> a) -> Expr a -> Expr a
Apply :: Expr (other -> a) -> Expr a -> Expr a
instance Functor Expr where
fmap fun val = Map fun val
instance Applicative Expr where
fun_expr <*> data_expr = Apply fun_expr data_expr
pure val = Constant val
-- And then some functions to optimize an Expr AST, evaluate Exprs, etc.
To make introspection nicer, I would like to print the values which are stored inside certain AST nodes of the DSL datatype.
However, in general any a might be stored in Constant, even those that do not implement Show. This is not necessarily a problem since we can constrain the instance of Expr like so:
instance Show a => Show (Expr a) where
...
This is not what I want however: I would still like to be able to print Expr even if a is not Show-able, by printing some placeholder value (such as just its type and a message that it is unprintable) instead.
So we want to do one thing if we have an a implementing Show, and another if a particular a does not.
Furthermore, the DSL also has the constructors Map and Apply which are even more problematic. The constructor is existential in other, and thus we cannot assume anything about other, a or (other -> a). Adding constraints to the type of other to the Map resp. Apply constructors would break the implementation of Functor resp. Applicative which forwards to them.
But here also I'd like to print for the functions:
a unique reference. This is always possible (even though it is not pretty as it requires unsafePerformIO) using System.Mem.StableName.
Its type, if possible (one technique is to use show (typeOf fun), but it requires that fun is Typeable).
Again we reach the issue where we want to do one thing if we have an f implementing Typeable and another if f does not.
How to do this?
Extra disclaimer: The goal here is not to create 'correct' Show instances for types that do not support it. There is no aspiration to be able to Read them later, or that print a != print b implies a != b.
The goal is to print any datastructure in a 'nice for human introspection' way.
The part I am stuck at, is that I want to use one implementation if extra constraints are holding for a resp. (other -> a), but a 'default' one if these do not exist.
Maybe type classes with FlexibleInstances, or maybe type families are needed here? I have not been able to figure it out (and maybe I am on the wrong track all together).
Not all problems have solutions. Not all constraint systems have a satisfying assignment.
So... relax the constraints. Store the data you need to make a sensible introspective function in your data structure, and use functions with type signatures like show, fmap, pure, and (<*>), but not exactly equal to them. If you need IO, use IO in your type signature. In short: free yourself from the expectation that your exceptional needs fit into the standard library.
To deal with things where you may either have an instance or not, store data saying whether you have an instance or not:
data InstanceOrNot c where
Instance :: c => InstanceOrNot c
Not :: InstanceOrNot c
(Perhaps a Constraint-kinded Either-alike, rather than Maybe-alike, would be more appropriate. I suspect as you start coding this you will discover what's needed.) Demand that clients that call notFmap and friends supply these as appropriate.
In the comments, I propose parameterizing your type by the constraints you demand, and giving a Functor instance for the no-constraints version. Here's a short example showing how that might look:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
import Data.Kind
type family All cs a :: Constraint where
All '[] a = ()
All (c:cs) a = (c a, All cs a)
data Lol cs a where
Leaf :: a -> Lol cs a
Fmap :: All cs b => (a -> b) -> Lol cs a -> Lol cs b
instance Functor (Lol '[]) where
fmap f (Leaf a) = Leaf (f a)
fmap f (Fmap g garg) = Fmap (f . g) garg
Great timing! Well-typed recently released a library which allows you to recover runtime information. They specifically have an example of showing arbitrary values. It's on github at https://github.com/well-typed/recover-rtti.
It turns out that this is a problem which has been recognized by multiple people in the past, known as the 'Constrained Monad Problem'. There is an elegant solution, explained in detail in the paper The Constrained-Monad Problem by Neil Sculthorpe and Jan Bracker and George Giorgidze and Andy Gill.
A brief summary of the technique: Monads (and other typeclasses) have a 'normal form'. We can 'lift' primitives (which are constrained any way we wish) into this 'normal form' construction, itself an existential datatype, and then use any of the operations available for the typeclass we have lifted into. These operations themselves are not constrained, and thus we can use all of Haskell's normal typeclass functions.
Finally, to turn this back into the concrete type (which again has all the constraints we are interested in) we 'lower' it, which is an operation that takes for each of the typeclass' operations a function which it will apply at the appropriate time.
This way, constraints from the outside (which are part of the functions supplied to the lowering) and constraints from the inside (which are part of the primitives we lifted) are able to be matched, and finally we end up with one big happy constrained datatype for which we have been able to use any of the normal Functor/Monoid/Monad/etc. operations.
Interestingly, while the intermediate operations are not constrained, to my knowledge it is impossible to write something which 'breaks' them as this would break the categorical laws that the typeclass under consideration should adhere to.
This is available in the constrained-normal Hackage package to use in your own code.
The example I struggled with, could be implemented as follows:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE UndecidableInstances #-}
module Example where
import Data.Dynamic
import Data.Kind
import Data.Typeable
import Control.Monad.ConstrainedNormal
-- | Required to have a simple constraint which we can use as argument to `Expr` / `Expr'`.
-- | This is definitely the part of the example with the roughest edges: I have yet to figure out
-- | how to make Haskell happy with constraints
class (Show a, Typeable a) => Introspectable a where {}
instance (Show a, Typeable a) => Introspectable a where {}
data Expr' (c :: * -> Constraint) a where
C :: a -> Expr' c a
-- M :: (a -> b) -> Expr' a -> Expr' b --^ NOTE: This one is actually never used as ConstrainedNormal will use the 'free' implementation based on A + C.
A :: c a => Expr' c (a -> b) -> Expr' c a -> Expr' c b
instance Introspectable a => Show (Expr' Introspectable a) where
show e = case e of
C x -> "(C " ++ show x ++ ")"
-- M f x = "(M " ++ show val ++ ")"
A fx x -> "(A " ++ show (typeOf fx) ++ " " ++ show x ++ ")"
-- | In user-facing code you'd not want to expose the guts of this construction
-- So let's introduce a 'wrapper type' which is what a user would normally interact with.
type Expr c a = NAF c (Expr' c) a
liftExpr :: c a => Expr' c a -> Expr c a
liftExpr expr = liftNAF expr
lowerExpr :: c a => Expr c a -> Expr' c a
lowerExpr lifted_expr = lowerNAF C A lifted_expr
constant :: Introspectable a => a -> Expr c a
constant val = pure val -- liftExpr (C val)
You could now for instance write
ghci> val = constant 10 :: Expr Introspectable Int
(C 10)
ghci> (+2) <$> val
(C 12)
ghci> (+) <$> constant 10 <*> constant 32 :: Expr Introspectable Int
And by using Data.Constraint.Trivial (part of the trivial-constrained library, although it is also possible to write your own 'empty constrained') one could instead write e.g.
ghci> val = constant 10 :: Expr Unconstrained Int
which will work just as before, but now val cannot be printed.
The one thing I have not yet figured out, is how to properly work with subsets of constraints (i.e. if I have a function that only requires Show, make it work with something that is Introspectable). Currently everything has to work with the 'big' set of constraints.
Another minor drawback is of course that you'll have to annotate the constraint type (e.g. if you do not want constraints, write Unconstrained manually), as GHC will otherwise complain that c0 is not known.
We've reached the goal of having a type which can be optionally be constrained to be printable, with all machinery that does not need printing to work also on all instances of the family of types including those that are not printable, and the types can be used as Monoids, Functors, Applicatives, etc just as you like.
I think it is a beautiful approach, and want to commend Neil Sculthorpe et al. for their work on the paper and the constrained-normal library that makes this possible. It's very cool!
Considering this simple example of ambiguous type inference:
#! /usr/bin/env stack
{- stack runghc -}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
main :: IO ()
main = do
-- This will fail to compile without additional type info
-- let w = read "22"
-- print w
-- My go-to for this is type signatures in the expressions
let x = read "33" :: Integer
print x
-- Another possibility is ScopedtypeVariables
let y :: Integer = read "44"
print y
-- How does TypeApplications differ from the code above? When should this be chosen instead?
let z = read #Integer "55"
print z
My question is, in cases like this, is there an advantage to using TypeApplications?
In almost all cases, it is an aesthetic choice only. I make the following additional comments for your consideration:
In all cases, if a thing typechecks with some collection of type signatures, there is a corresponding collection of type applications that also causes that term to typecheck (and with the same choices of instance dictionaries, etc.).
In cases where either a signature or an application can be used, the code produced by GHC will be identical.
Some ambiguous types cannot be resolved via signatures, and type applications must be used. For example, foo :: (Monoid a, Monoid b) => b cannot be given a type signature that determines a. (This bullet motivates the "almost" in the first sentence of this answer. No other bullet motivates the "almost".)
Type applications are frequently syntactically lighter than type signatures. For example, when the type is long, or a type variable is mentioned several times. Some comparisons:
showsPrec :: Int -> Bool -> String -> String
showsPrec #Bool
sortOn :: Ord b => (Int -> b) -> [Int] -> [Int]
sortOn #Int
Sometimes it is possible to shuffle the type signature around to a different subterm so that you need only give a short signature with little repetition. But then again... sometimes not.
Sometimes, the signature or application is intended to convey some information to the reader or encourage a certain way of thinking about a piece of code (i.e. is not strictly for compiler consumption). If part of that information involves attaching the annotation in a specific code location, your options may be somewhat constrained.
I want to create a Complex type to represent complex numbers.
Following works:
Prelude> data Complex = Complex Int Int
Prelude> :t Complex
Complex :: Int -> Int -> Complex
How can I change this to accept any Num type, instead of just Int.
I tried following:
Prelude> data Complex a = Num a => Complex a a
but got this:
* Data constructor `Complex' has existential type variables, a context, or a specialised result type
Complex :: forall a. Num a => a -> a -> Complex a
(Use ExistentialQuantification or GADTs to allow this)
* In the definition of data constructor `Complex'
In the data type declaration for `Complex'
I'm not really sure what to make of this error. Any help is appreciated.
Traditional data in Haskell is just that: data. It doesn't need to know anything about the properties of its fields, it just needs to be able to store them. Hence there's no real need to constrain the fields at that point; just make it
data Complex a = Complex !a !a
(! because strict fields are better for performance).
Of course when you then implement the Num instance, you will need a constraint:
instance (Num a) => Num (Complex a) where
fromInteger = (`Complex`0) . fromInteger
Complex r i + Complex ρ ι = Complex (r+ρ) (i+ι)
...
...in fact, you need the much stronger constraint RealFloat a to implement abs, at least that's how the standard version does it. (Which means, Complex Int is actually not usable, not with the standard Num hierarchy; you need e.g. Complex Double.)
That said, it is also possible to bake the constraint in to the data type itself. The ExistentialTypes syntax you tried is highly limiting though and not suitable for this; what you want instead is the GADT
data Complex a where
Complex :: Num a => a -> a -> Complex a
With that in place, you could then implement e.g. addition without mentioning any constraint in the signature
cplxAdd :: Complex a -> Complex a -> Complex a
cplxAdd (Complex r i) (Complex ρ ι) = Complex (r+ρ) (i+ι)
You would now need to fulfill Num whenever you try to construct a Complex value though. That means, you'd still need an explicit constraint in the Num instance.
Also, this version is potentially much slower, because the Num dictionary actually needs to be stored in the runtime representation.
Type constructors cannot be constrained in pure Haskell, only functions can. So it is supposed that you declare
data Complex a = Complex a a
and then constrain functions, like
conjugate :: (Num a) => Complex a -> Complex a
conjugate (Complex x y) = Complex x (-y)
In fact, the type and constraint for conjugate can be derived by the compiler, so you can just define the implementation:
conjugate (Complex x y) = Complex x (-y)
However, if you really wish to constrain the type constructor Complex, you can turn on some extensions that enable it, namely ExistentialQuantification or GADTs, as the compiler suggests. To do this, add this line to the very beginning of your file:
{-# LANGUAGE ExistentialQuantification #-}
or
{-# LANGUAGE GADTs #-}
Those are called pragmas.
While you could, as the compiler message instructs, use ExistentialQuantification, you could also define the type like this:
data Complex a = Complex a a deriving (Show, Eq)
It's a completely unconstrained type, so perhaps another name would be more appropriate... This type seems to often be called Pair...
When you write functions, however, you can constrain the values contained in the type:
myFunction :: Num a => Complex a -> a
myFunction (Complex x y) = x + y
I am a new learner of Haskell, my code is as follows:
data Num a=>Units a = Units a (SymbolicManip a )
deriving (Eq)
I am not sure how to fix it?
Anyone can help me?
Typeclass contexts in datatypes are now regarded as a not so useful feature. The problem is that the following does not compile:
foo :: Units a -> a
foo (Units x _) = x+x
This intuitively should compile, since the Units a argument can only be constructed for a type a satisfying Num a. So, on destruction (pattern matching) one should be able to access the Num a instance. However this is not the case, and a Num a must be counterintuitively provided on destruction as well:
foo :: Num a => Units a -> a
foo (Units x _) = x+x
The standard suggestion is therefore to remove the constraint Num a from the Units a datatype declaration, and add it instead to every function involving Units a.
Another option is to enable GADTs and change the datatype to:
data Units a where
Units :: Num a => a -> SymbolicManip a -> Units a
This does the "right" thing: a Num a instance is required to construct a value, and is instead provided on destruction. In this way, the first foo declaration above will be well-typed.
I almost forgot the "quick & dirty" option, which is to enable the obsolescent datatype context feature: this is done by adding at the beginning of your file the line
{-# LANGUAGE DatatypeContexts #-}
Still, I would rather modify the code than to enable this language extension.
Is it possible to do the following:
foo = bar
where
type A = (Some, Huge, Type, Sig)
meh :: A -> (A, A) -> A
I only need to use this custom type inside the where clause, so it does not make sense to define it globally.
This isn't possible. Why not just define it above the function? You don't have to export it from the module (just use an explicit export list).
By the way, if you really do have a type that big, it's probably a sign that you should factor it into smaller parts, especially if you have a lot of tuples as your example suggests; data-types would be more appropriate.
Actually, there's one, slightly ridiculous, way to approximate this:
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
foo :: forall abbrv. (abbrv ~ (Some, Huge, Type, Sig))
=> abbrv -> abbrv
foo x = meh x (x, x)
where meh :: abbrv -> (abbrv, abbrv) -> abbrv
meh x y = {- ... -}
I can't really recommend enabling two language extensions just for the sake of abbreviating types in signatures, though if you're already using them (or GADTs instead of type families) I suppose it doesn't really hurt anything.
Silliness aside, you should consider refactoring your types in cases like this, as ehird suggests.