List of any `DataKind` in GADT - haskell

Disclaimer
GADTs & DataKinds are unexplored territory for me, so some of the limitations and capabilities of them are unknown to me.
The Question
So I'm writing an AST for a JavaScript code emitter, and I've identified one edge case between expressions and that is that they can either be a reference or not. So I've used GADTS and datakinds to type this aspect of JavaScript expression semantics. The ast looks something like this.
Subset of the expression AST
-- at the moment I'm just using a bool to identify if the expression
-- behaves as an reference, but I'll probably change it due to the fact
-- a bool is pretty vague
data JSExp :: Bool -> * where
JSNumber :: Double -> JSExp False
JSBool :: Bool -> JSExp False
JSReference :: Text -> JSExp True
JSProperty :: JSExp a -> Text -> JSExp True
JSAssign :: JSExp True -> JSExp b -> JSExp b
This looks all fine and dandy, because an assignment expression requires the first expression to be a reference like a property expression ("test".shadyProperty) or a reference/identifier.
The problem
Now I want to add an array literal expression, in JavaScript it shouldn't matter what is in this list so lists like this are legal
[a, 1, true, a.b]
In my AST however this is not legal because there are multiple types in the list
data JSExp :: Bool -> * where
-- ...
JSArray :: [JSExp ???] -> JSExp False
let aref = JSReference "a"
in JSArray [aref, JSNumber 2, JSBool True, JSProp aref "b"]
What goes in the place of ??? for the type? A similar problem occurs when I want to build out a constructor for JSObject's and JSFunctionCall's, as these are also expressions.
Idris's soultion
In Idris ??? would look something like this.
data JSExp : Bool -> Type where
JSArray : List (JSExp _) -> JSExp False
JSNumber : Float -> JSExp False
JSBool : Bool -> JSExp False
-- ...
Potenial soultions
Wrapping type
One soultion that isn't like the Idris one, would to have a wrapper type like this
data JSExpWrap = Refs (JSExp True) | NoRef (JSExp False)
This would make the api of my library gross and it's not what I'm looking for.
In Summary
I'm looking for the equivalent that's found in Idris, and an explanation of the implications of the solution. If there's no equivalent, a solution of the next best thing is what I'm looking for.

You can use existentials:
{-# LANGUAGE GADTs, DataKinds, PolyKinds #-}
data Exists :: (k -> *) -> * where
This :: p x -> Exists p
data JSExp :: Bool -> * where
...
JSArray :: [Exists JSExp] -> JSExp False
test = let aref = JSReference "a"
in JSArray [This aref, This (JSNumber 2), This (JSBool True), This (JSProperty aref "b")]
Or with some sugar:
infixr 5 !:
(!:) :: p x -> [Exists p] -> [Exists p]
x !: xs = This x : xs
test = let aref = JSReference "a"
in JSArray $ aref !: JSNumber 2 !: JSBool True !: JSProperty aref "b" !: []

Related

Pattern synonym with "virtual" arg and conjunction

Let's say I have types:
data Some a = S1 a | S2 a
data BigRec = BigRec {
some :: Some [Int]
, ...
}
and I want a pattern synonym that will have a "virtual argument", like
pattern BadBigRec t <- ...
where t is True or False which depends on the value of some: if it's S1 something-particular - it's True, else if it is S2 something-other, it's False. I hit 2 problems: I don't know how to have an "argument" in a pattern synonym which is only in the left hand side and it is not presented in the right hand side (t is "virtual" and it does not exist in BigRec or Some). And the second problem, I don't know how to express "OR" (conjunction) in pattern synonyms. Not just pattern that match, but a conjunct of predicates.
Is it possible with pattern synonyms or only pattern views are useful for such cases?
As mentioned in the comments, you can use a view pattern.
{-# LANGUAGE ViewPatterns, PatternSynonyms #-}
data Some a = S1 a | S2 a
data BigRec = BigRec {
some :: Some [Int]
}
pattern BadBigRec :: Bool -> BigRec
pattern BadBigRec t <- (checkBad -> t)
-- modify this as needed
checkBad :: BigRec -> Bool
checkBad x = case some x of
S1 _ -> True
S2 _ -> False

DataKind Unions

I'm not sure if it is the right terminology, but is it possible to declare function types that take in an 'union' of datakinds?
For example, I know I can do the following:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
...
data Shape'
= Circle'
| Square'
| Triangle'
data Shape :: Shape' -> * where
Circle :: { radius :: Int} -> Shape Circle'
Square :: { side :: Int} -> Shape Square'
Triangle
:: { a :: Int
, b :: Int
, c :: Int}
-> Shape Triangle'
test1 :: Shape Circle' -> Int
test1 = undefined
However, what if I want to take in a shape that is either a circle or a square? What if I also want to take in all shapes for a separate function?
Is there a way for me to either define a set of Shape' kinds to use, or a way for me to allow multiple datakind definitions per data?
Edit:
The usage of unions doesn't seem to work:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
...
type family Union (a :: [k]) (r :: k) :: Constraint where
Union (x ': xs) x = ()
Union (x ': xs) y = Union xs y
data Shape'
= Circle'
| Square'
| Triangle'
data Shape :: Shape' -> * where
Circle :: { radius :: Int} -> Shape Circle'
Square :: { side :: Int} -> Shape Square'
Triangle
:: { a :: Int
, b :: Int
, c :: Int}
-> Shape Triangle'
test1 :: Union [Circle', Triangle'] s => Shape s -> Int
test1 Circle {} = undefined
test1 Triangle {} = undefined
test1 Square {} = undefined
The part above compiles
You can accomplish something like this in (I think) a reasonably clean way using a type family together with ConstraintKinds and PolyKinds:
type family Union (a :: [k]) (r :: k) :: Constraint where
Union (x ': xs) x = ()
Union (x ': xs) y = Union xs y
test1 :: Union [Circle', Triangle'] s => Shape s -> Int
test1 = undefined
The () above is the empty constraint (it's like an empty "list" of type class constraints).
The first "equation" of the type family makes use of the nonlinear pattern matching available in type families (it uses x twice on the left hand side). The type family also makes use of the fact that if none of the cases match, it will not give you a valid constraint.
You should also be able to use a type-level Boolean instead of ConstraintKinds. That would be a bit more cumbersome and I think it would be best to avoid using a type-level Boolean here (if you can).
Side-note (I can never remember this and I had to look it up for this answer): You get Constraint in-scope by importing it from GHC.Exts.
Edit: Partially disallowing unreachable definitions
Here is a modification to get it to (partially) disallow unreachable definitions as well as invalid calls. It is slightly more roundabout, but it seems to work.
Modify Union to give a * instead of a constraint, like this:
type family Union (a :: [k]) (r :: k) :: * where
Union (x ': xs) x = ()
Union (x ': xs) y = Union xs y
It doesn't matter too much what the type is, as long as it has an inhabitant you can pattern match on, so I give back the () type (the unit type).
This is how you would use it:
test1 :: Shape s -> Union [Circle', Triangle'] s -> Int
test1 Circle {} () = undefined
test1 Triangle {} () = undefined
-- test1 Square {} () = undefined -- This line won't compile
If you forget to match on it (like, if you put a variable name like x instead of matching on the () constructor), it is possible that an unreachable case can be defined. It will still give a type error at the call-site when you actually try to reach that case, though (so, even if you don't match on the Union argument, the call test1 (Square undefined) () will not type check).
Note that it seems the Union argument must come after the Shape argument in order for this to work (fully as described, anyway).
This is getting kind of awful, but I guess you could require a proof that it's either a circle or a square using Data.Type.Equality:
test1 :: Either (s :~: Circle') (s :~: Square') -> Shape s -> Int
Now the user has to give an extra argument (a "proof term") saying which one it is.
In fact you can use the proof term idea to "complete" bradm's solution, with:
class MyOpClass sh where
myOp :: Shape sh -> Int
shapeConstraint :: Either (sh :~: Circle') (sh :~: Square')
Now nobody can go adding any more instances (unless they use undefined, which would be impolite).
You could use typeclasses:
class MyOpClass sh where
myOp :: Shape sh -> Int
instance MyOpClass Circle' where
myOp (Circle r) = _
instance MyOpClass Square' where
myOP (Square s) = _
This doesn't feel like a particularly 'complete' solution to me - anyone could go back and add another instance MyOpClass Triangle' - but I can't think of any other solution. Potentially you could avoid this problem simply by not exporting the typeclass however.
Another solution I've noticed, though pretty verbose, is to create a kind that has a list of feature booleans. You can then pattern match on the features when restricting the type:
-- [circleOrSquare] [triangleOrSquare]
data Shape' =
Shape'' Bool
Bool
data Shape :: Shape' -> * where
Circle :: { radius :: Int} -> Shape (Shape'' True False)
Square :: { side :: Int} -> Shape (Shape'' True True)
Triangle
:: { a :: Int
, b :: Int
, c :: Int}
-> Shape (Shape'' False True)
test1 :: Shape (Shape'' True x) -> Int
test1 Circle {} = 2
test1 Square {} = 2
test1 Triangle {} = 2
Here, Triangle will fail to match:
• Couldn't match type ‘'True’ with ‘'False’
Inaccessible code in
a pattern with constructor:
Triangle :: Int -> Int -> Int -> Shape ('Shape'' 'False 'True),
in an equation for ‘test1’
• In the pattern: Triangle {}
In an equation for ‘test1’: test1 Triangle {} = 2
|
52 | test1 Triangle {} = 2
| ^^^^^^^^^^^
Unfortunately, I don't think you can write this as a record, which may be clearer and avoids the ordering of the features.
This might be usable in conjunction with the class examples for readability.

Is it possible to overload logical operators (&& e.g.) in Haskell?

I'm working with many-valued logic and trying to overload basic logic functions.
I haven't problem with overloading Num and Eq operators, but I don't know how to overload &&, || and not.
Is it possible? Thanks for answers!
Haskell doesn't really have overloading (=ad-hoc-polymorphism) at all. +, * etc. are not functions but class methods: “overloading” them is more like defining concrete descendants of an OO interface / purely-abstract class than overloading functions in, say, C++.
The logical operators OTOH are just ordinary functions, which are defined in the Prelude once and for all.
However, in Haskell, infix operators are mostly treated just as a special kind of function name, they're not part of the actual syntax definition. Nothing prevents you from defining new, different operators with the same purpose, e.g.
class Booly b where
true :: b
false :: b
(&&?) :: b -> b -> b
(||?) :: b -> b -> b
infixr 3 &&?
infixr 2 ||?
instance Booly Bool where
true = True
false = False
(&&?) = (&&)
(||?) = (||)
instance Booly MVBool where
true = ...
In fact, it's enough if the new names are disambiguated by module qualifiers:
import Prelude hiding ((&&), (||))
import qualified Prelude
class Booly b where
true :: b
false :: b
(&&) :: b -> b -> b
(||) :: b -> b -> b
infixr 3 &&
infixr 2 ||
instance Booly Bool where
true = True
false = False
(&&) = (Prelude.&&)
(||) = (Prelude.||)
There is no such thing as overriding in Haskell in the monkeypatching sense.
There's also no way to hook an extension into something that wasn't built to be extended.
You can simply shadow the definition of e.g. && but this would 1) not affect the semantics of && in other modules and 2) would be confusing.
So I would use something as simple as:
-- laws should be defined for the class and instances QuickChecked/proved against these laws
class Logic a where
(&.&) :: a -> a -> a
(|.|) :: a -> a -> a
...
instance Logic Bool where
(&.&) = (&&)
(|.|) = (||)
data MultiBool = False' | True' | Perhaps | CouldBe | Possibly | Unlikely
instance Logic MultiBool where
...
No, it's not possible. The type of && is Bool -> Bool -> Bool, and Haskell doesn't allow ad-hoc overloading. You can shadow the declaration, but then you can't use the operator for both booleans and your mvl values in the same module without qualification.
I recommend you define similar-looking operators such as &&? for your mvls.
You can't override them, but you can define your own.
infixr 3 <&&> <||>
<&&> :: ??? -> ??? -> ???
<&&> ...
(&&) is defined as
(&&) :: Bool -> Bool -> Bool
So unless you don't load the Prelude, or load it qualified, you cannot overload that operator.
There is however a typeclass that does more or less what you are looking for: Data.Bits with signatures like:
(.&.) :: Bits a => a -> a -> a
(.|.) :: Bits a => a -> a -> a
complement :: Bits a => a -> a
Data.Bits is normally used to represent bitwise operations. You could decide to ignore the remaining operators (return some default value) or assign a useful property to it.
Otherwise you can define similar operators. In that case one betters defines a typeclass first:
class Logic a where
land :: a -> a -> a
lor :: a -> a -> a
lnot :: a -> a
lnand :: a -> a -> a
lnand x = lnot . land x
lnor :: a -> a -> a
lnor x = lnot . lor x

Test if a value matches a constructor

Say I have a data type like so:
data NumCol = Empty |
Single Int |
Pair Int Int |
Lots [Int]
Now I wish to filter out the elements matching a given constructor from a [NumCol]. I can write it for, say, Pair:
get_pairs :: [NumCol] -> [NumCol]
get_pairs = filter is_pair
where is_pair (Pair _ _) = True
is_pair _ = False
This works, but it's not generic. I have to write a separate function for is_single, is_lots, etc.
I wish instead I could write:
get_pairs = filter (== Pair)
But this only works for type constructors that take no arguments (i.e. Empty).
So the question is, how can I write a function that takes a value and a constructor, and returns whether the value matches the constructor?
At least get_pairs itself can be defined relatively simply by using a list comprehension to filter instead:
get_pairs xs = [x | x#Pair {} <- xs]
For a more general solution of matching constructors, you can use prisms from the lens package:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import Control.Lens.Extras (is)
data NumCol = Empty |
Single Int |
Pair Int Int |
Lots [Int]
-- Uses Template Haskell to create the Prisms _Empty, _Single, _Pair and _Lots
-- corresponding to your constructors
makePrisms ''NumCol
get_pairs :: [NumCol] -> [NumCol]
get_pairs = filter (is _Pair)
Tags of tagged unions ought to be first-class values, and with a wee bit of effort, they are.
Jiggery-pokery alert:
{-# LANGUAGE GADTs, DataKinds, KindSignatures,
TypeFamilies, PolyKinds, FlexibleInstances,
PatternSynonyms
#-}
Step one: define type-level versions of the tags.
data TagType = EmptyTag | SingleTag | PairTag | LotsTag
Step two: define value-level witnesses for the representability of the type-level tags. Richard Eisenberg's Singletons library will do this for you. I mean something like this:
data Tag :: TagType -> * where
EmptyT :: Tag EmptyTag
SingleT :: Tag SingleTag
PairT :: Tag PairTag
LotsT :: Tag LotsTag
And now we can say what stuff we expect to find associated with a given tag.
type family Stuff (t :: TagType) :: * where
Stuff EmptyTag = ()
Stuff SingleTag = Int
Stuff PairTag = (Int, Int)
Stuff LotsTag = [Int]
So we can refactor the type you first thought of
data NumCol :: * where
(:&) :: Tag t -> Stuff t -> NumCol
and use PatternSynonyms to recover the behaviour you had in mind:
pattern Empty = EmptyT :& ()
pattern Single i = SingleT :& i
pattern Pair i j = PairT :& (i, j)
pattern Lots is = LotsT :& is
So what's happened is that each constructor for NumCol has turned into a tag indexed by the kind of tag it's for. That is, constructor tags now live separately from the rest of the data, synchronized by a common index which ensures that the stuff associated with a tag matches the tag itself.
But we can talk about tags alone.
data Ex :: (k -> *) -> * where -- wish I could say newtype here
Witness :: p x -> Ex p
Now, Ex Tag, is the type of "runtime tags with a type level counterpart". It has an Eq instance
instance Eq (Ex Tag) where
Witness EmptyT == Witness EmptyT = True
Witness SingleT == Witness SingleT = True
Witness PairT == Witness PairT = True
Witness LotsT == Witness LotsT = True
_ == _ = False
Moreover, we can easily extract the tag of a NumCol.
numColTag :: NumCol -> Ex Tag
numColTag (n :& _) = Witness n
And that allows us to match your specification.
filter ((Witness PairT ==) . numColTag) :: [NumCol] -> [NumCol]
Which raises the question of whether your specification is actually what you need. The point is that detecting a tag entitles you an expectation of that tag's stuff. The output type [NumCol] doesn't do justice to the fact that you know you have just the pairs.
How might you tighten the type of your function and still deliver it?
One approach is to use DataTypeable and the Data.Data module. This approach relies on two autogenerated typeclass instances that carry metadata about the type for you: Typeable and Data. You can derive them with {-# LANGUAGE DeriveDataTypeable #-}:
data NumCol = Empty |
Single Int |
Pair Int Int |
Lots [Int] deriving (Typeable, Data)
Now we have a toConstr function which, given a value, gives us a representation of its constructor:
toConstr :: Data a => a -> Constr
This makes it easy to compare two terms just by their constructors. The only remaining problem is that we need a value to compare against when we define our predicate! We can always just create a dummy value with undefined, but that's a bit ugly:
is_pair x = toConstr x == toConstr (Pair undefined undefined)
So the final thing we'll do is define a handy little class that automates this. The basic idea is to call toConstr on non-function values and recurse on any functions by first passing in undefined.
class Constrable a where
constr :: a -> Constr
instance Data a => Constrable a where
constr = toConstr
instance Constrable a => Constrable (b -> a) where
constr f = constr (f undefined)
This relies on FlexibleInstance, OverlappingInstances and UndecidableInstances, so it might be a bit evil, but, using the (in)famous eyeball theorem, it should be fine. Unless you add more instances or try to use it with something that isn't a constructor. Then it might blow up. Violently. No promises.
Finally, with the evil neatly contained, we can write an "equal by constructor" operator:
(=|=) :: (Data a, Constrable b) => a -> b -> Bool
e =|= c = toConstr e == constr c
(The =|= operator is a bit of a mnemonic, because constructors are syntactically defined with a |.)
Now you can write almost exactly what you wanted!
filter (=|= Pair)
Also, maybe you'd want to turn off the monomorphism restriction. In fact, here's the list of extensions I enabled that you can just use:
{-# LANGUAGE DeriveDataTypeable, FlexibleInstances, NoMonomorphismRestriction, OverlappingInstances, UndecidableInstances #-}
Yeah, it's a lot. But that's what I'm willing to sacrifice for the cause. Of not writing extra undefineds.
Honestly, if you don't mind relying on lens (but boy is that dependency a doozy), you should just go with the prism approach. The only thing to recommend mine is that you get to use the amusingly named Data.Data.Data class.

Checking for a particular data constructor

Let's say that I defined my own data-Type like
data MyData = A arg| B arg2| C arg3
How would I write a function (for instance: isMyDataType) that checks wether the given argument is one out of the particular types in MyData and successively returns a boolean (True or False) , e.g. typing in Ghci:
isMyDataType B returns True and isMyDataType Int returns False.
I believe you want functions to test for particular constructors:
isA :: MyData -> Bool
isB :: MyData -> Bool
If so, then you can write these yourself or derive them. The implementation would look like:
isA (A _) = True
isA _ = False
isB (B _) = True
isB _ = False
To derive them automatically, just use the derive library and add, in your source code:
{-# LANGUAGE TemplateHaskell #-}
import Data.DeriveTH
data MyData = ...
deriving (Eq, Ord, Show}
derive makeIs ''MyData
-- Older GHCs require more syntax: $( derive makeIs ''MyData)
Also note: your data declaration is invalid, the name must be capitalized, MyData instead of myData.
Finally, this whole answer is based on the assumption you want to test constructors, not data types as you said (which are statically checked at compile time, as Tarrasch said).
Haskell always checks that the types makes sense. The compiler would complain immediately if you wrote isMyDataType 4, because 4 is not of type MyData, it's of type Int.
I'm not sure this is what you asked for, but either way I strongly suggest for you to try out what you've asked here in practice, so you can see for yourself. Most important is that you check out type signatures in haskell, it is key for learning haskell.
You can use Maybes. You can create a set of functions that check for each of the types
getA, getB, getC :: MyData a -> Maybe a
getA x = case x of {(A v) -> Just v; _ -> Nothing}
getB x = case x of {(B v) -> Just v; _ -> Nothing}
getC x = case x of {(C v) -> Just v; _ -> Nothing}
This affords some practical idioms for certain tasks:
allAs :: [MyData a] -> [a]
allAs xs = mapMaybe getA xs
printIfA :: Show a => MyData a -> IO ()
printIfA x = maybe (return ()) print $ getA x

Resources