Unsafe entailment with Haskell constraints - haskell

I'm playing around with the constraints package (for GHC Haskell). I have a type family for determining if a type-level list contains an element:
type family HasElem (x :: k) (xs :: [k]) where
HasElem x '[] = False
HasElem x (x ': xs) = True
HasElem x (y ': xs) = HasElem x xs
This works, but one thing it doesn't give me is the knowledge that
HasElem x xs entails HasElem x (y ': xs)
since the type family isn't an inductive definition of the "is element of" statement (like you would have in agda). I'm pretty sure that, until GADTs are promotable to the type level, there is no way to express list membership with a data type.
So, I've used the constraints package to write this:
containerEntailsLarger :: Proxy x -> Proxy xs -> Proxy b -> (HasElem x xs ~ True) :- (HasElem x (b ': xs) ~ True)
containerEntailsLarger _ _ _ = unsafeCoerceConstraint
Spooky, but it works. I can pattern match on the entailment to get what I need. What I'm wondering is if it can ever cause a program to crash. It seems like it couldn't, since unsafeCoerceConstraint is defined as:
unsafeCoerceConstraint = unsafeCoerce refl
And in GHC, the type level is elided at runtime. I thought I'd check though, just to make sure that doing this is ok.
--- EDIT ---
Since no one has given an explanation yet, I thought I would expand the question a little. In the unsafe entailment I'm creating, I only expect a type family. If I did something that involved typeclass dictionaries instead like this:
badEntailment :: Proxy a -> (Show a) :- (Ord a)
badEntailment _ = unsafeCoerceConstraint
I assume that this would almost certainly be capable of causing a segfault. Is this true? and if so, what makes it different from the original?
--- EDIT 2 ---
I just wanted to provide a little background for why I am interested in this. One of my interests is making a usable encoding of relational algebra in Haskell. I think that no matter how you define functions to work on type-level lists, there will be obvious things that aren't proved correctly. For example, a constraint (for semijoin) that I've had before looked like this (this is from memory, so it might not be exact):
semijoin :: ( GetOverlap as bs ~ Overlap inAs inBoth inBs
, HasElem x as, HasElem x (inAs ++ inBoth ++ inBs)) => ...
So, it should be obvious (to a person) that if I take union of two sets, that it contains an element x that was in as, but I'm not sure that it's possible the legitimately convince the constraint solver of this. So, that's my motivation for doing this trick. I create entailments to cheat the constraint solver, but I don't know if it's actually safe.

I don't know if this will suit your other needs, but it accomplishes this particular purpose. I'm not too good with type families myself, so it's not clear to me what your type family can actually be used for.
{-# LANGUAGE ...., UndecidableInstances #-}
type family Or (x :: Bool) (y :: Bool) :: Bool where
Or 'True x = 'True
Or x 'True = 'True
Or x y = 'False
type family Is (x :: k) (y :: k) where
Is x x = 'True
Is x y = 'False
type family HasElem (x :: k) (xs :: [k]) :: Bool where
HasElem x '[] = 'False
HasElem x (y ': z) = Or (Is x y) (HasElem x z)
containerEntailsLarger :: proxy1 x -> proxy2 xs -> proxy3 b ->
(HasElem x xs ~ 'True) :- (HasElem x (b ': xs) ~ 'True)
containerEntailsLarger _p1 _p2 _p3 = Sub Dict
An approach using GADTs
I've been having trouble letting go of this problem. Here's a way to use a GADT to get good evidence while using type families and classes to get a good interface.
-- Lots of extensions; I don't think I use ScopedTypeVariables,
-- but I include it as a matter of principle to avoid getting
-- confused.
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-}
{-# LANGUAGE TypeFamilies, TypeOperators #-}
{-# LANGUAGE DataKinds, PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE GADTs #-}
-- Some natural numbers
data Nat = Z | S Nat deriving (Eq, Ord, Show)
-- Evidence that a type is in a list of types
data ElemG :: k -> [k] -> * where
Here :: ElemG x (x ': xs)
There :: ElemG x xs -> ElemG x (y ': xs)
deriving instance Show (ElemG x xs)
-- Take `ElemG` to the class level.
class ElemGC (x :: k) (xs :: [k]) where
elemG :: proxy1 x -> proxy2 xs -> ElemG x xs
-- There doesn't seem to be a way to instantiate ElemGC
-- directly without overlap, but we can do it via another class.
instance ElemGC' n x xs => ElemGC x xs where
elemG = elemG'
type family First (x :: k) (xs :: [k]) :: Nat where
First x (x ': xs) = 'Z
First x (y ': ys) = 'S (First x ys)
class First x xs ~ n => ElemGC' (n :: Nat) (x :: k) (xs :: [k]) where
elemG' :: proxy1 x -> proxy2 xs -> ElemG x xs
instance ElemGC' 'Z x (x ': xs) where
elemG' _p1 _p2 = Here
instance (ElemGC' n x ys, First x (y ': ys) ~ 'S n) => ElemGC' ('S n) x (y ': ys) where
elemG' p1 _p2 = There (elemG' p1 Proxy)
This actually seems to work, at least in simple cases:
*Hello> elemG (Proxy :: Proxy Int) (Proxy :: Proxy '[Int, Char])
Here
*Hello> elemG (Proxy :: Proxy Int) (Proxy :: Proxy '[Char, Int, Int])
There Here
*Hello> elemG (Proxy :: Proxy Int) (Proxy :: Proxy '[Char, Integer, Int])
There (There Here)
This doesn't support the precise entailment you desire, but I believe the ElemGC' recursive case is probably the closest you can get with such an informative constraint, at least in GHC 7.10.

Related

How to properly wrap over a datatype indexed by an inductive datatype?

I'm trying to thinly wrap around a singleton version of a list. I have trouble deconstructing it. Here's a minimal implementation:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ExplicitForAll #-}
module InductiveWrapper where
import Data.Kind (Type)
import Data.Proxy (Proxy)
import GHC.Prim (coerce)
data List a = Nil | Cons a (List a)
data SList :: [ k ] -> Type where
SNil :: SList '[]
SCons :: Proxy k -> SList ks -> SList (k ': ks)
newtype Set a = S [ a ]
data SSet :: Set k -> Type where
SS :: SList xs -> SSet ('S xs)
type family Add (el :: k) (set :: Set k) :: Set k where
Add el ('S xs) = 'S (el ': xs)
uncons :: forall k (el :: k) (set :: Set k)
. SSet (Add el set) -> (Proxy el, SSet set)
uncons (SS (x `SCons` xs)) = (x, SS xs)
Here's the relevant bit of the error:
Could not deduce: set ~ 'S ks
from the context: Add el set ~ 'S xs
bound by a pattern with constructor:
SS :: forall k (xs :: [k]). SList xs -> SSet ('S xs),
in an equation for ‘uncons’
[...]
or from: xs ~ (k1 : ks)
bound by a pattern with constructor:
SCons :: forall k1 (k2 :: k1) (ks :: [k1]).
Proxy k2 -> SList ks -> SList (k2 : ks),
[...]
• Relevant bindings include
xs :: SList ks (bound at InductiveWrapper.hs:37:29)
x :: Proxy k1 (bound at InductiveWrapper.hs:37:19)
xs' :: SList xs (bound at InductiveWrapper.hs:37:14)
s :: SSet (Add el set) (bound at InductiveWrapper.hs:37:8)
The problem as I understand is that Add el set gets stuck because the type checker doesn't understand that the only way set could be constructed is by using 'S.
How do I unstuck it or resolve this problem by other means? Apart from using type instead of newtype. The whole reason I'm doing this is to completely hide the use of [ k ] and SList.
Type families are non-injective, which technically means that you can't go from result to arguments, right to left. Except not. GHC 8.0 introduced TypeFamilyDependencies, which lets you specify injectivity for type families, like this:
type family Add (el :: k) (set :: Set k) = (set' :: Set k) | set' -> el set where
Add el ('S xs) = 'S (el ': xs)
However, for some reason that I don't yet completely understand, this still doesn't work in your case, causing the same issue. I suspect it may have something to do with the fact that the list in question is double wrapped, not sure.
But I do have another workaround: you can ditch the whole injectivity issue and specify your type family the other way around - from the list to the tuple. Except you'd need two type families - one for head and one for tail:
type family Head set where Head ('S (el ': xs)) = el
type family Tail set where Tail ('S (el ': xs)) = 'S xs
uncons :: SSet set -> (Proxy (Head set), SSet (Tail set))
uncons (SS (x `SCons` xs)) = (x, SS xs)
But this seems a bit overengineered to me. If you just need to uncons these type sets, I would go with a good ol' type class, which has the unbeatable advantage of wrapping types and values together, so you don't have to jump through hoops to match them manually:
class Uncons set res | set -> res where
uncons :: SSet set -> res
instance Uncons ('S (el ': xs)) (Proxy el, SSet ('S xs)) where
uncons (SS (x `SCons` xs)) = (x, SS xs)

Defining arity-generic lift

I'm trying to define liftN for Haskell. The value-level implementation in dynamically typed languages like JS is fairly straightforward, I'm just having trouble expressing it in Haskell.
After some trial and error, I arrived at the following, which typechecks (note the entire implementation of liftN is undefined):
{-# LANGUAGE FlexibleContexts, ScopedTypeVariables, TypeFamilies, TypeOperators, UndecidableInstances #-}
import Data.Proxy
import GHC.TypeLits
type family Fn x (y :: [*]) where
Fn x '[] = x
Fn x (y:ys) = x -> Fn y ys
type family Map (f :: * -> *) (x :: [*]) where
Map f '[] = '[]
Map f (x:xs) = (f x):(Map f xs)
type family LiftN (f :: * -> *) (x :: [*]) where
LiftN f (x:xs) = (Fn x xs) -> (Fn (f x) (Map f xs))
liftN :: Proxy x -> LiftN f x
liftN = undefined
This gives me the desired behavior in ghci:
*Main> :t liftN (Proxy :: Proxy '[a])
liftN (Proxy :: Proxy '[a]) :: a -> f a
*Main> :t liftN (Proxy :: Proxy '[a, b])
liftN (Proxy :: Proxy '[a, b]) :: (a -> b) -> f a -> f b
and so on.
The part I'm stumped on is how to actually implement it. I was figuring maybe the easiest way is to exchange the type level list for a type level number representing its length, use natVal to get the corresponding value level number, and then dispatch 1 to pure, 2 to map and n to (finally), the actual recursive implementation of liftN.
Unfortunately I can't even get the pure and map cases to typecheck. Here's what I added (note go is still undefined):
type family Length (x :: [*]) where
Length '[] = 0
Length (x:xs) = 1 + (Length xs)
liftN :: (KnownNat (Length x)) => Proxy x -> LiftN f x
liftN (Proxy :: Proxy x) = go (natVal (Proxy :: Proxy (Length x))) where
go = undefined
So far so good. But then:
liftN :: (Applicative f, KnownNat (Length x)) => Proxy x -> LiftN f x
liftN (Proxy :: Proxy x) = go (natVal (Proxy :: Proxy (Length x))) where
go 1 = pure
go 2 = fmap
go n = undefined
...disaster strikes:
Prelude> :l liftn.hs
[1 of 1] Compiling Main ( liftn.hs, interpreted )
liftn.hs:22:28: error:
* Couldn't match expected type `LiftN f x'
with actual type `(a0 -> b0) -> (a0 -> a0) -> a0 -> b0'
The type variables `a0', `b0' are ambiguous
* In the expression: go (natVal (Proxy :: Proxy (Length x)))
In an equation for `liftN':
liftN (Proxy :: Proxy x)
= go (natVal (Proxy :: Proxy (Length x)))
where
go 1 = pure
go 2 = fmap
go n = undefined
* Relevant bindings include
liftN :: Proxy x -> LiftN f x (bound at liftn.hs:22:1)
|
22 | liftN (Proxy :: Proxy x) = go (natVal (Proxy :: Proxy (Length x))) where
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.
At this point it isn't clear to me what exactly is ambiguous or how to disambiguate it.
Is there a way to elegantly (or if not-so-elegantly, in a way that the inelegance is constrained to the function implementation) implement the body of liftN here?
There are two issues here:
You need more than just the natVal of a type-level number to ensure the whole function type checks: you also need a proof that the structure you're recursing on corresponds to the type-level number you're referring to. Integer on its own loses all of the type-level information.
Conversely, you need more runtime information than just the type: in Haskell, types have no runtime representation, so passing in a Proxy a is the same as passing in (). You need to get in runtime info somewhere.
Both of these problems can be addressed using singletons, or with classes:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
data Nat = Z | S Nat
type family AppFunc f (n :: Nat) arrows where
AppFunc f Z a = f a
AppFunc f (S n) (a -> b) = f a -> AppFunc f n b
type family CountArgs f where
CountArgs (a -> b) = S (CountArgs b)
CountArgs result = Z
class (CountArgs a ~ n) => Applyable a n where
apply :: Applicative f => f a -> AppFunc f (CountArgs a) a
instance (CountArgs a ~ Z) => Applyable a Z where
apply = id
{-# INLINE apply #-}
instance Applyable b n => Applyable (a -> b) (S n) where
apply f x = apply (f <*> x)
{-# INLINE apply #-}
-- | >>> lift (\x y z -> x ++ y ++ z) (Just "a") (Just "b") (Just "c")
-- Just "abc"
lift :: (Applyable a n, Applicative f) => (b -> a) -> (f b -> AppFunc f n a)
lift f x = apply (fmap f x)
{-# INLINE lift #-}
This example is adapted from Richard Eisenberg's thesis.

Extensible records (I think)

What I roughly want is this:
data A = ...
data B = ...
data C = ...
class HasA t where
getA :: t -> A
class HasB t where
getB :: t -> B
class HasC t where
getC :: t -> C
So I can do something like this (pseudocode follows):
a :: A
b :: B
x = mkRecord { elemA a, elemB b }
y = mkRecord { elemB b, elemA a }
-- type of `x` == type of `y`
Naturally, only the appropriate get functions work, in the above case getA and getB.
I'd also like the following functions
slice :: Subset a b => a -> b
slice x = -- just remove the bits of x that aren't in type b.
add :: e -> a -> a ++ e
add e x = -- add an element to the "record" (compile error if it's already there)
I feel like this is not a new problem so perhaps a resolution for this already exists. Note that I don't require the solution to be extensible, the amount of types I need to deal with is finite and known, but of course and extensible one wouldn't hurt.
I've found a couple of packages that seem to be in the field of what I'm looking for, namely HList and extensible (perhaps extensible is better because I want my records unordered). I got a bit lost in the Hackage docs so I'd like just some sample code (or a link to some sample code) that roughly achieves what I'm looking for.
This is exactly what HList is good for. However, since I don't have the right setup to test something with the HList package right now (and besides, it has more confusing data definitions), here is a minimal example of HList that uses singletons for the type-level list stuff.
{-# LANGUAGE DataKinds, TypeOperators, GADTs,TypeFamilies, UndecidableInstances,
PolyKinds, FlexibleInstances, MultiParamTypeClasses
#-}
import Data.Singletons
import Data.Promotion.Prelude.List
data HList (l :: [*]) where
HNil :: HList '[]
HCons :: x -> HList xs -> HList (x ': xs)
The add function is the simplest: it is just HCons:
add :: x -> HList xs -> HList (x ': xs)
add = HCons
Something more interesting is combining two records:
-- Notice we are using `:++` from singletons
combine :: HList xs -> HList ys -> HList (xs :++ ys)
combine HNil xs = xs
combine (x `HCons` xs) ys = x `HCons` (xs `combine` ys)
Now, for your get function, you need to dispatch based on the type-level list. To do this, you need an overlapping type class.
class Has x xs where
get :: xs -> x
instance {-# OVERLAPS #-} Has x (HList (x ': xs)) where
get (x `HCons` _) = x
instance Has x (HList xs) => Has x (HList (y ': xs)) where
get (_ `HCons` xs) = get xs
Finally, we can use Has to define a similar Subset class. Same idea as before.
class Subset ys xs where
slice :: xs -> ys
instance Subset (HList '[]) (HList xs) where
slice _ = HNil
instance (Get y (HList xs), Subset (HList ys) (HList xs)) =>
Subset (HList (y ': ys)) (HList xs) where
slice xs = get xs `HCons` slice xs
As you mention in parens, the simple HList form does not ensure you have only one of any type of field (so get just returns the first field, ignoring the rest). If you want uniqueness, you can just add a constraint to the HList constructor.
data Record (l :: [*]) where
Nil :: Record '[]
Cons :: (NotElem x xs ~ 'True) => x -> Record xs -> Record (x ': xs)
However, defining Subset using Record looks like it involves some proofs. :)

Ambiguous type variable in length of heterogeneous list

I have an issue with ambiguous type variables when computing the length of a heterogeneous list. The problem seems to be that the length-function is not polymorphic in the HList's elements.
My Code
First, all the language extensions I'm using:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
Natural numbers are defined like so:
data Nat = Zero | Succ Nat
data HNat n where
HZero :: HNat Zero
HSucc :: HNat n -> HNat (Succ n)
The heterogeneous list is defined as a GADT
data HList (ts :: [*]) where
HNil :: HList '[]
(:::) :: t -> HList ts -> HList (t ': ts)
infixr 5 :::
We can query the length of an HList:
class HLength xs n | xs -> n where
hLength :: HList xs -> HNat n
instance HLength '[] Zero where
hLength HNil = HZero
instance HLength xs n => HLength (x ': xs) (Succ n) where
hLength (_ ::: xs) = HSucc (hLength xs)
We can index into an HList and retrieve the i-th element:
class HIndex xs i y | xs i -> y where
hIndex :: HList xs -> HNat i -> y
instance HIndex (x ': xs) Zero x where
hIndex (x ::: xs) HZero = x
instance HIndex xs i y => HIndex (x ': xs) (Succ i) y where
hIndex (x ::: xs) (HSucc i) = hIndex xs i
The Issue
With that established I'll demonstrate the issue.
Suppose I construct an HList containing a function that itself takes an HList and does something with its first element.
test1 = ((\l n -> l `hIndex` HZero || n == 0) ::: HNil)
In this case the first element has to be a Bool. The derived type-signature confirms that constraint:
:: (Eq a, Num a, HIndex xs 'Zero Bool) =>
HList '[HList xs -> a -> Bool]
Now I would like to calculate the length of the list test:
test2 = hLength test1
Unfortunately, this fails to compile with the following error message:
HListConstraints.hs:55:17:
No instance for (HIndex xs0 'Zero Bool)
arising from a use of ‘test1’
The type variable ‘xs0’ is ambiguous
Note: there is a potential instance available:
instance HIndex (x : xs) 'Zero x
-- Defined at HListConstraints.hs:42:10
In the first argument of ‘hLength’, namely ‘test1’
In the expression: hLength test1
In an equation for ‘test2’: test2 = hLength test1
Failed, modules loaded: none.
The constraint on the list elements causes ambiguous type variables.
My understanding is that I need to make hLength polymorphic in the elements of the list that is passed to it. How can I do that?
The problem isn't with HLength, and it's already as polymorphic as it can be. The issue is with HIndex, which is unnecessarily specific in the xs parameter.
From HIndex xs 'Zero Bool we should be able to infer that xs has Bool ': xs' shape for some xs'. Since the HIndex implementation is driven by the Nat class parameter, we can leave the other parameters unspecified in the instance heads, and instead refine them in the instance constraints, enabling GHC to do the mentioned inference:
class HIndex xs i y | xs i -> y where
hIndex :: HList xs -> HNat i -> y
instance (xs ~ (x ': xs')) => HIndex xs Zero x where
hIndex (x ::: xs) HZero = x
instance (xs ~ (y ': xs'), HIndex xs' i x) => HIndex xs (Succ i) x where
hIndex (x ::: xs) (HSucc i) = hIndex xs i
After this:
test1 :: (Eq a, Num a) => HList '[HList (Bool : xs') -> a -> Bool]
The HIndex constraint disappears, and the remaining constraints are resolved when Num a makes a default to Integer:
> :t hLength test1
hLength test1 :: HNat ('Succ 'Zero)
The general rule when computing with type classes is to move type dependencies into instance constraints, and only do those pattern matches in instance heads which are essential to instance definitions. This turns instance head matching problems (which immediately fail when parameters aren't of the right form) into constraint solving problems (which can be lazily solved based on information from other parts of the program).
Alternatively, we could explicitly define the type of test1 to be test1 :: HList ((HList (Bool ': xs) -> Int -> Bool) ': '[]) alone.
This allows us to leave the instance definitions as they were, and do away with the type equality constraints of form xs ~ (x ': xs') which András proposes.
The choice is then whether you want to have to explicitly define the type of test1. The type equality constraints make it possible to define test1 without also providing a type annotation.

Type families for dummies

Could someone give a super simple (few line) example to get a basic understanding about what type families can be used for and what are they ?
The 2+2 kind of example of type families ?
Here's an example:
{-# Language TypeFamilies, DataKinds, KindSignatures, GADTs, UndecidableInstances #-}
data Nat = Z | S Nat
type family Plus (x :: Nat) (y :: Nat) :: Nat where
Plus 'Z y = y
Plus ('S x) y = 'S (Plus x y)
data Vec :: Nat -> * -> * where
Nil :: Vec 'Z a
Cons :: a -> Vec n a -> Vec ('S n) a
append :: Vec m a -> Vec n a -> Vec (Plus m n) a
append Nil ys = ys
append (Cons x xs) ys = Cons x (append xs ys)
Note that many/most interesting applications of type families require UndecidableInstances. You should not be scared of this extension.
Another useful sort of type family is one associated with a class. For a really contrived example,
class Box b where
type Elem b :: *
elem :: b -> Elem b
An instance of Box is a type that something can be pulled out of. For instance,
instance Box (Identity x) where
type Elem (Identity x) = x
elem = runIdentity
instance Box Char where
type Elem Char = String
elem c = [c]
Now elem (Identity 3) = 3 and elem 'x' = "x".
You can also use type families to make weird skolem variables. This is best done in the as-yet-unreleased GHC 8.0.1, where it will look like
type family Any :: k where {}
Any is a peculiar type. It's uninhabited, it can't be (specifically) an instance of a class, and it's poly-kinded. This turns out to be really useful for certain purposes. This particular type is advertised as a safe target for unsafeCoerce, but Data.Constraint.Forall uses similar type families for more interesting purposes.

Resources