Haskell simplify Dependent Type function signature - haskell

This is an example of dependent type haskell function dfunc. Its type depends on the argument value: zero maps to value of Int one maps to value of Integer and so on as defined in type family below.
So I have function dfunc with signature GADTInt a -> Dt a
But I want to get rid of this wierd GADTInt a encoding of int and implement a function with type signature Int -> Dt a I understand that a type is to generic for this.., but I try to cheat with typeclass NTI and function ff without type signature, to let haskell do type inference for me, and get error, as i understand because ff implementation inexpressible in a type signature.
Can I do some "magic" here to get rid of GADTInt a -> Dt a and replace it with Int -> ?
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
module Main where
data Nat :: * where
Z :: Nat
S :: Nat -> Nat
data GADTInt a where
GADTZero :: GADTInt Z
GADTOne :: GADTInt (S Z)
GADTTwo :: GADTInt (S (S Z))
GADTThree :: GADTInt (S (S (S Z)))
type family Dt a where
Dt 'Z = Int
Dt ('S 'Z) = Integer
Dt ('S ('S 'Z)) = Float
Dt ('S ('S ('S 'Z))) = String
class NTI a where
toi :: a -> Int
instance NTI (GADTInt a) where
toi GADTZero = 0
toi GADTOne = 1
toi GADTTwo = 2
toi GADTThree = 3
dfunc :: GADTInt a -> Dt a
dfunc GADTZero = 1
dfunc GADTOne = 1000000000000000000000000000000
dfunc GADTTwo = 3.14
dfunc GADTThree = "Hi"
ff i
| toi GADTZero == i = dfunc GADTZero
| toi GADTOne == i = dfunc GADTOne
| toi GADTTwo == i = dfunc GADTTwo
| toi GADTThree == i = dfunc GADTThree
| otherwise = undefined

The only way to use a Dt a is to know what a is. Because types are erased, you will need a run-time representation of it, for example using the GADTInt singleton. You can package those in an existential type:
data SomeDt where
SomeDt :: GADTInt a -> Dt a -> SomeDt
It's also useful to wrap the singleton in another existential type:
data SomeGADTInt where
SomeGADTInt :: GADTInt a -> SomeGADTInt
so that you can define the "non-dependent" tagging:
toTag :: Int -> SomeGADTInt
toTag 0 = SomeGADTInt GADTZero
toTag 1 = SomeGADTInt GADTOne
toTag 2 = SomeGADTInt GADTTwo
toTag 3 = SomeGADTInt GADTThree
wrap dfunc:
someDFunc :: SomeGADTInt -> SomeDt
someDFunc (SomeGADTInt i) = SomeDt i (dfunc i)
compose:
ff :: Int -> SomeDt
ff = someDFunc . toTag

Related

Generalizing extending a type by a data family

I'm writing a library where I'm extending several types of kind * -> * -> * into type families of kind k -> k -> k (for certain k) using a data family:
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators #-}
-- ...
type family (+) = (c :: k -> k -> k) | c -> k where
(+) = Either
(+) = Sum_
infixl 6 +
data family Sum_ (a :: j -> k) (b :: j -> k) :: j -> k
newtype instance Sum_ a b x = Sum1 { getSum1 :: a x + b x }
newtype instance Sum_ a b x y = Sum2 { getSum2 :: a x y + b x y }
newtype instance Sum_ a b x y z = Sum3 { getSum3 :: a x y z + b x y z }
type family (×) = (p :: k -> k -> k) | p -> k where
(×) = (,)
(×) = Product_
infixl 7 ×
data family Product_ (a :: j -> k) (b :: j -> k) :: j -> k
newtype instance Product_ a b x = Product1 { getProduct1 :: a x × b x }
newtype instance Product_ a b x y = Product2 { getProduct2 :: a x y × b x y }
newtype instance Product_ a b x y z = Product3 { getProduct3 :: a x y z × b x y z }
This works great, but I can't help but notice repetition in how I'm extending each type, so naturally I want to capture that pattern and use it to cut down on boilerplate:
type family Extension (base :: * -> * -> *) = (p :: k -> k -> k) | p -> k where
Extension base = base
Extension base = Extension_ base
data family Extension_ (base :: * -> * -> *) (a :: j -> k) (b :: j -> k) :: j -> k
newtype instance Extension_ base a b x = Extension1 { getExtension1 :: Extension base (a x) (b x) }
newtype instance Extension_ base a b x y = Extension2 { getExtension2 :: Extension base (a x y) (b x y) }
newtype instance Extension_ base a b x y z = Extension3 { getExtension3 :: Extension base (a x y z) (b x y z) }
type (+) = Extension Either
infixl 6 +
type (×) = Extension (,)
infixl 7 +
type (^) = Extension (Dual (->))
infixr 8 ^
Unfortunately, this appears to violate injectivity as GHC understands it (though, as far as I can tell, there's only one possible k for each Extension base):
src/Extension.hs:36:3: error:
• Type family equation violates injectivity annotation.
RHS of injective type family equation is a bare type variable
but these LHS type and kind patterns are not bare variables: ‘*’
Extension base = base
-- Defined at src/Extension.hs:36:3
• In the equations for closed type family ‘Extension’
In the type family declaration for ‘Extension’
|
36 | Extension base = base
| ^^^^^^^^^^^^^^^^^^^^^
src/Extension.hs:36:3: error:
• Type family equations violate injectivity annotation:
Extension base = base
-- Defined at src/Extension.hs:36:3
Extension base = Extension_ base
-- Defined at src/Extension.hs:37:3
• In the equations for closed type family ‘Extension’
In the type family declaration for ‘Extension’
|
36 | Extension base = base
| ^^^^^^^^^^^^^^^^^^^^^
If I ditched the type family and went for a pure data family solution, I wouldn't have this problem, but I don't want to introduce newtype wrappers around the * -> * -> * kinded type operators.
Is there another way I can capture and reuse the Extension pattern?

Church encoding for dependent types: from Coq to Haskell

In Coq I can define a Church encoding for lists of length n:
Definition listn (A : Type) : nat -> Type :=
fun m => forall (X : nat -> Type), X 0 -> (forall m, A -> X m -> X (S m)) -> X m.
Definition niln (A : Type) : listn A 0 :=
fun X n c => n.
Definition consn (A : Type) (m : nat) (a : A) (l : listn A m) : listn A (S m) :=
fun X n c => c m a (l X n c).
Is the type system of Haskell (including its extensions) strong enough to accommodate such definitions? If yes, how?
Sure it is:
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
import Data.Kind -- Needed for `Type`
data Nat = Z | S Nat -- Roll your own...
type List (a :: Type) (n :: Nat) =
forall (x :: Nat -> Type). x Z -> (forall (m :: Nat). a -> x m -> x (S m)) -> x n
niln :: List a Z
niln = \z _ -> z
consn :: a -> List a n -> List a (S n)
consn a l = \n c -> c a (l n c)
Further proof (for skeptics) of the isomorphism with the usual GADT formulation:
data List' (a :: Type) (n :: Nat) where
Nil :: List' a Z
Cons :: a -> List' a m -> List' a (S m)
to :: List' a n -> List a n
to Nil = niln
to (Cons a l) = consn a (to l)
from :: List a n -> List' a n
from l = l Nil Cons

Singletons of singletons (emulating complex pi types in Haskell)

I've got a simple proof of concept in Idris which uses dependent types to enforce some not-too-complex business logic. A few names have been changed to protect the not-so-innocent, but the idea is that we want to collect "lines" in a sequence. Each line pertains to a specific section, but only one (EconProduction) has anything we care about yet. In general, lines have a section-specific keyword and an expression whose form/type may depend upon the keyword used.
For this particular section, each line either describes some numbers for a "phase" (Prod), or continues the last named "phase" (Continue).
In Idris, we can do this like so:
data EconSection
= EconGeneral
| EconProduction
data EconPhase
= Oil
| Water
| NumPhase Nat
data ContState
= ContNone
| ContProd EconPhase
data Keyword : EconSection -> ContState -> ContState -> Type where
Prod : (p : EconPhase) -> Keyword EconProduction c (ContProd p)
Continue : Keyword s c c
data Expression : (s : EconSection) ->
(d : ContState) ->
Keyword s c d ->
Type where
ExProc : Double -> Double -> Expression EconProduction (ContProd p) k
data Line : EconSection -> ContState -> ContState -> Type where
L : (k : Keyword s c d) -> Expression s d k -> Line s c d
data Lines : EconSection -> ContState -> Type where
First : Line s ContNone d -> Lines s d
Then : Lines s c -> Line s c d -> Lines s d
infixl 0 `Then`
good : Lines EconProduction (ContProd (NumPhase 1))
good = First (L (Prod Oil) (ExProc 23.2 70.1))
`Then` (L (Continue) (ExProc 27.9 1.2))
`Then` (L (Prod (NumPhase 1)) (ExProc 91.2 7014.1))
`Then` (L (Continue) (ExProc 91.2 7014.1))
So far so good! The usual dependent typestate business. For eminently practical business reasons, we wish to actually implement this logic in GHC Haskell. I've built it with singletons (rolled my own as-needed rather than use the singletons package, just for a short proof of concept):
{-# LANGUAGE GADTs, KindSignatures, DataKinds #-}
{-# LANGUAGE RankNTypes, TypeInType, TypeOperators #-}
{-# LANGUAGE TypeFamilies, TypeFamilyDependencies, MultiParamTypeClasses #-}
import Data.Kind (Type)
data Nat
= Z
| S Nat
data SNat :: Nat -> Type where
SZ :: SNat 'Z
SS :: SNat n -> SNat ('S n)
data SSNat :: forall (n :: Nat) . SNat n -> Type where
SSZ :: SSNat 'SZ
SSS :: SSNat n -> SSNat ('SS n)
type family SingNat (n :: Nat) :: SNat n where
SingNat 'Z = 'SZ
SingNat ('S n) = 'SS (SingNat n)
data EconSection
= EconGeneral
| EconProduction
data SEconSection :: EconSection -> Type where
SEconGeneral :: SEconSection 'EconGeneral
SEconProduction :: SEconSection 'EconProduction
type family SingSection (s :: EconSection) :: SEconSection s where
SingSection 'EconGeneral = 'SEconGeneral
SingSection 'EconProduction = 'SEconProduction
data EconPhase
= Oil
| Water
| NumPhase Nat
data SEconPhase :: EconPhase -> Type where
SOil :: SEconPhase 'Oil
SWater :: SEconPhase 'Water
SNumPhase :: SNat n -> SEconPhase ('NumPhase n)
data SSEconPhase :: forall (p :: EconPhase) . SEconPhase p -> Type where
SSOil :: SSEconPhase 'SOil
SSWater :: SSEconPhase 'SWater
SSNumPhase :: SSNat n -> SSEconPhase ('SNumPhase n)
type family SingEconPhase (p :: EconPhase) :: SEconPhase p where
SingEconPhase 'Oil = 'SOil
SingEconPhase 'Water = 'SWater
SingEconPhase ('NumPhase n) = 'SNumPhase (SingNat n)
data ContState
= ContNone
| ContProd EconPhase
data SContState :: ContState -> Type where
SContNone :: SContState 'ContNone
SContProd :: SEconPhase p -> SContState ('ContProd p)
type family SingContState (c :: ContState) :: SContState c where
SingContState 'ContNone = 'SContNone
SingContState (ContProd p) = 'SContProd (SingEconPhase p)
data Keyword :: EconSection -> ContState -> ContState -> Type where
Prod :: SEconPhase p -> Keyword 'EconProduction c ('ContProd p)
Continue :: Keyword s c c
data SKeyword :: forall (s :: EconSection) (c :: ContState) (d :: ContState) .
Keyword s c d -> Type where
SProd :: SSEconPhase p -> SKeyword ('Prod p)
SContinue :: SKeyword 'Continue
data Expression :: forall (s :: EconSection) (c :: ContState) (d :: ContState) .
SEconSection s -> SContState d -> Keyword s c d -> Type where
ExProc :: Double -> Double -> Expression SEconProduction (SContProd p) k
type family KWSection k where
KWSection (Keyword s _ _) = s
type family KWFrom k where
KWFrom (Keyword _ c _) = c
type family KWTo k where
KWTo (Keyword _ _ d) = d
data Line :: EconSection -> ContState -> ContState -> Type where
L :: SKeyword (k :: Keyword s c d)
-> Expression (SingSection s) (SingContState d) k
-> Line s c d
data Lines :: EconSection -> ContState -> Type where
First :: Line s 'ContNone d -> Lines s d
Then :: Lines s c -> Line s c d -> Lines s d
infixl 0 `Then`
good :: Lines 'EconProduction ('ContProd ('NumPhase ('S 'Z)))
good = First (L (SProd SSOil) (ExProc 23.2 70.1))
`Then` (L (SContinue) (ExProc 27.9 1.2))
`Then` (L (SProd (SSNumPhase (SSS SSZ))) (ExProc 91.2 7014.1))
`Then` (L (SContinue) (ExProc 91.2 7014.1))
Here's my question. Is there any way to avoid the "singletons of singletons"? I don't like the look of things like SSNat and so on at all, but this is where I get by translating every pi type to an extra layer of singleton-ing. I haven't been able to make any simpler approach work, and I've not seen any clever ideas in the singletons package to make this easier, though I might have easily missed something beneath all the Template Haskell.
Yes.
Consider that, by the definition of a singleton type, you have as much type information in a singleton as the singleton of that singleton, because they both have a unique instance.
In your code
With the above in mind, we can remove the SSNat and SSEconPhase declarations from your code. Then, in the SProd constructor
SProd :: SSEconPhase p - > SKeyword ('Prod p)
We know that SEconPhase would be sufficient to decide p, so we can rewrite it as
SProd :: SEconPhase p - > SKeyword ('Prod p)
Which produces a kind error - what we need is a type transformation like
SomeType :: (p :: EconPhase) -> SEconPhase p
Which you already have defined in your code as SingEconPhase. The result is
SProd :: SEconPhase p - > SKeyword ('Prod (SingEconPhase p))
In general
You should never have to write a singleton of a singleton - if you need to "lift" a type parameter to the singleton type, then the correct choice is to write a type family as you have done.

Implementing functional addition in Haskell

I was given a puzzle to do the following in Haskell,
f takes two functions, function a and function b. Function a takes na inputs and returns a Num type and function b takes nb inputs and returns a Num type. f returns a new function of arity na+nb that applies a to the first na arguments, nb to the rest of the arguments and returns their sum.
In mathematics I would write this as:
My first naïve attempt at this in Haskell was:
f a b = flip ((+) . a) . b
But this only works if a is a unary function.
After this I thought about the puzzle for a long while without being able to come up with even an idea for how I might do this. This is the first time in a long time I have been this utterly stumped in Haskell.
How might I solve this puzzle? Is there a solution to this puzzle? (I was given this puzzle by a friend and I don't believe they had a actual solution in mind at the time)
Here's a pretty simple approach using type families that works monomorphically in the numeric type (e.g., specialized to Int). We'll need a few extensions:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
The function f will be defined in a type class:
class VarArgs r s where
type F r s
f :: r -> s -> F r s
and we'll handle the following cases. If the type of the first function is of form a :: Int -> r, we'll use the following instance to gobble an argument x and feed it to a:
instance VarArgs r s => VarArgs (Int -> r) s where
type F (Int -> r) s = Int -> F r s
f :: (Int -> r) -> s -> Int -> F r s
f a b x = f (a x) b
This has the effect of recursing on the type of a until it's of the form Int. Then, we'll use a similar instance to recurse on the type b :: Int -> s:
instance VarArgs Int s => VarArgs Int (Int -> s) where
type F Int (Int -> s) = Int -> F Int s
f :: Int -> (Int -> s) -> Int -> F Int s
f a b x = f a (b x)
Ultimately, both functions will be reduced to 0-ary functions of type a, b :: Int, and we can use the terminal instance:
instance VarArgs Int Int where
type F Int Int = Int
f :: Int -> Int -> Int
f a b = a + b
Here's a little test to prove it works:
times2 :: Int -> Int -> Int
times2 x y = x * y
times3 :: Int -> Int -> Int -> Int
times3 x y z = x * y * z
foo :: [Int]
foo = [ f times2 times2 1 2 3 4
, f times2 times3 1 2 3 4 5
, f times3 times2 1 2 3 4 5
, f times3 times3 1 2 3 4 5 6]
and loading this into GHCi gives:
> foo
[14,62,26,126]
>
Generalizing this to be polymorphic in any Num type doesn't seem to be straightforward. Replacing the Int type with a constrained Num n type leads to errors regarding conflicting family instance declarations.
This is easy and simple - much simpler than #K.A.Buhr's type family approach, in my opinion - if you tweak your representation of an n-ary function, instead using a unary function of an n-dimensional vector.
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
import Prelude hiding (splitAt)
import Data.Bifunctor
The usual suspects: (type-level) natural numbers, their (value-level) singletons, type-level addition, and vectors.
data Nat = Z | S Nat
data Natty n where
Zy :: Natty Z
Sy :: Natty n -> Natty (S n)
type family n + m where
Z + m = m
S n + m = S (n + m)
data Vec n a where
Nil :: Vec Z a
(:>) :: a -> Vec n a -> Vec (S n) a
splitAt takes a runtime Natty - it needs to know at runtime where to split the vector - and a vector that is at least as long as the Natty.
splitAt :: Natty n -> Vec (n + m) a -> (Vec n a, Vec m a)
splitAt Zy xs = (Nil, xs)
splitAt (Sy n) (x :> xs) =
let (ys, zs) = splitAt n xs
in (x :> ys, zs)
Then your f, which I'm calling splitApply, is a straightforward application of splitAt.
splitApply :: Natty n -> (Vec n a -> b) -> (Vec m a -> c) -> Vec (n + m) a -> (b, c)
splitApply at f g xs = bimap f g $ splitAt at xs
(I haven't bothered to show the "add the results" part, because it's so simple that I bored myself writing it. You could argue that, since Hask is a monoidal category, (,) represents a sort of addition anyway.)

How to deconstruct an SNat (singletons)

I am experimenting with depedent types in Haskell and came across the following in the paper of the 'singletons' package:
replicate2 :: forall n a. SingI n => a -> Vec a n
replicate2 a = case (sing :: Sing n) of
SZero -> VNil
SSucc _ -> VCons a (replicate2 a)
So I tried to implement this myself, just toget a feel of how it works:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.Singletons
import Data.Singletons.Prelude
import Data.Singletons.TypeLits
data V :: Nat -> * -> * where
Nil :: V 0 a
(:>) :: a -> V n a -> V (n :+ 1) a
infixr 5 :>
replicateV :: SingI n => a -> V n a
replicateV = replicateV' sing
where replicateV' :: Sing n -> a -> V n a
replicateV' sn a = case sn of
SNat -> undefined -- what can I do with this?
Now the problem is that the Sing instance for Nat does not have SZero or SSucc. There is only one constructor called SNat.
> :info Sing
data instance Sing n where
SNat :: KnownNat n => Sing n
This is different than other singletons that allow matching, such as STrue and SFalse, such as in the following (useless) example:
data Foo :: Bool -> * -> * where
T :: a -> Foo True a
F :: a -> Foo False a
foo :: forall a b. SingI b => a -> Foo b a
foo a = case (sing :: Sing b) of
STrue -> T a
SFalse -> F a
You can use fromSing to get a base type, but this of course does allow GHC to check the type of the output vector:
-- does not typecheck
replicateV2 :: SingI n => a -> V n a
replicateV2 = replicateV' sing
where replicateV' :: Sing n -> a -> V n a
replicateV' sn a = case fromSing sn of
0 -> Nil
n -> a :> replicateV2 a
So my question: how to implement replicateV?
EDIT
The answer given by erisco explains why my approach of deconstructing an SNat does not work. But even with the type-natural library, I am unable to implement replicateV for the V data type using GHC's build-in Nat types.
For example the following code compiles:
replicateV :: SingI n => a -> V n a
replicateV = replicateV' sing
where replicateV' :: Sing n -> a -> V n a
replicateV' sn a = case TN.sToPeano sn of
TN.SZ -> undefined
(TN.SS sn') -> undefined
But this does not seem to give enough information to the compiler to infer whether n is 0 or not. For example the following gives a compiler error:
replicateV :: SingI n => a -> V n a
replicateV = replicateV' sing
where replicateV' :: Sing n -> a -> V n a
replicateV' sn a = case TN.sToPeano sn of
TN.SZ -> Nil
(TN.SS sn') -> undefined
This gives the following error:
src/Vec.hs:25:28: error:
• Could not deduce: n1 ~ 0
from the context: TN.ToPeano n1 ~ 'TN.Z
bound by a pattern with constructor:
TN.SZ :: forall (z0 :: TN.Nat). z0 ~ 'TN.Z => Sing z0,
in a case alternative
at src/Vec.hs:25:13-17
‘n1’ is a rigid type variable bound by
the type signature for:
replicateV' :: forall (n1 :: Nat) a1. Sing n1 -> a1 -> V n1 a1
at src/Vec.hs:23:24
Expected type: V n1 a1
Actual type: V 0 a1
• In the expression: Nil
In a case alternative: TN.SZ -> Nil
In the expression:
case TN.sToPeano sn of {
TN.SZ -> Nil
(TN.SS sn') -> undefined }
• Relevant bindings include
sn :: Sing n1 (bound at src/Vec.hs:24:21)
replicateV' :: Sing n1 -> a1 -> V n1 a1 (bound at src/Vec.hs:24:9)
So, my original problem still remains, I am still unable to do anything usefull with the SNat.
There are two notions of naturals at play here. One is "literal naturals" (i.e. 0, 1, 2, and so on) and the other is "Peano naturals" (i.e. Z, S Z, S (S Z), and so on). The one the paper is using is clearly Peano naturals but the one singletons uses is literal naturals.
Thankfully there is another package called type-natural which defines Peano naturals as well as conversion to literal naturals and conversion from literal naturals.
From the comments, I'm worried I must be missing something terrifically obvious, but here's my take on it. The whole point of:
replicate2 :: forall n a. SingI n => a -> Vec a n
replicate2 a = case (sing :: Sing n) of
SZero -> VNil
SSucc _ -> VCons a (replicate2 a)
is that, in order to return VNil :: Vec a 0 when the function has general return type Vec a n, you need to specialize the n to 0, and pattern-matching on GADTs provides a way to do this, as long as you have a constructor, like SZero, that implies n ~ 0.
Now the SNats in the singleton package have no such constructor. The only way to get one, as far as I can see, is to build a whole new singleton type for naturals and implement the necessary type families. Maybe you can do it in a way that wraps the Nats, so you're closer to SZero :: Sing (SN 0), SNonZero :: Sing (SN n) than a Peano construction, but I don't know.
Of course, there's another way to specialize a function that returns Vec a n to return Vec a 0, namely type classes.
If you are willing to abandon some of the explicit singleton machinery and switch to type classes (and also allow overlapping and undecidable instances), the following seems to work. I had to slightly modify the definition of V to use n :- 1 instead of n :+ 1, but I don't think that poses a problem.
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE UndecidableInstances #-}
import Data.Singletons
import Data.Singletons.Prelude
import Data.Singletons.TypeLits
data V :: Nat -> * -> * where
Nil :: V 0 a
(:>) :: a -> V (n :- 1) a -> V n a
infixr 5 :>
class VC n a where
replicateV :: a -> V n a
instance VC 0 a where
replicateV _ = Nil
instance VC (n :- 1) a => VC n a where
replicateV x = x :> replicateV x
instance (Show a) => Show (V n a) where
show Nil = "Nil"
show (x :> v) = show x ++ " :> " ++ show v
headV :: V (n :+ 1) a -> a
headV (x :> _) = x
tailV :: ((n :+ 1) :- 1) ~ n => V (n :+ 1) a -> V n a
tailV (_ :> v) = v
main = do print (replicateV False :: V 0 Bool)
print (replicateV 1 :: V 1 Int)
print (replicateV "Three" :: V 3 String)

Resources