Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data type parametrized by constant in Haskell

I would like to define a data type in Haskell which is parametrized by an Int constant along the lines:

data Q (n :: Int) = Q n (Int,Int) -- non-working code

in order to allow me to define functions of the type:

addQ :: (Q n)->(Q n)->(Q n)
addQ (Q k (i1,j1)) (Q k (i2,j2))) = Q k (i1+i2,j1+j2)

The idea is that in this way I am able to restrict addition to Q's that have the same n. Intuitively, it feels that this should be possible, but so far all my (admittedly newbie) attempts have stranded on the rigors of GHC.

like image 910
BeMuSeD Avatar asked Mar 15 '21 15:03

BeMuSeD


People also ask

How do you define a data type in Haskell?

The Data Keyword and Constructors In general, we define a new data type by using the data keyword, followed by the name of the type we're defining. The type has to begin with a capital letter to distinguish it from normal expression names. To start defining our type, we must provide a constructor.

How do types work in Haskell?

In Haskell, every statement is considered as a mathematical expression and the category of this expression is called as a Type. You can say that "Type" is the data type of the expression used at compile time. To learn more about the Type, we will use the ":t" command.

How does show work in Haskell?

The result of show is a syntactically correct Haskell expression containing only constants, given the fixity declarations in force at the point where the type is declared. It contains only the constructor names defined in the data type, parentheses, and spaces.


2 Answers

As the comments say, this is possible with the DataKinds extension (technically it's possible to achieve something very similar without it, but it's very unergonomic).

{-# LANGUAGE DataKinds, ExplicitForAll, KindSignatures #-}

import GHC.TypeLits (Nat)
data Q (n :: Nat) = Q Int

addQ :: forall (n :: Nat). Q n -> Q n -> Q n
addQ (Q x) (Q y) = Q (x + y)

let q1 = Q 1 :: Q 3
    q2 = Q 2 :: Q 3
in addQ q1 q2
-- => Q 3 :: Q 3

If you put the KnownNat constraint on n (also from GHC.TypeLits) you can also get the n as a regular term using the natVal function.

like image 54
Cubic Avatar answered Sep 20 '22 17:09

Cubic


Based on the hints and answer kindly supplied, I can give the answer I was looking for, including an example of how to unpack n from the parametrized data type.

{-# LANGUAGE DataKinds, ExplicitForAll, KindSignatures #-}

import GHC.TypeLits (Nat,KnownNat,natVal)

data Q (n :: Nat) = Q (Int,Int)

instance (KnownNat n) => Show (Q n) where
    show q@(Q (i,j)) = "("++(show i)++","++(show j)++")::"++(show (natVal q))

addQ :: forall (n :: Nat). Q n -> Q n -> Q n
addQ (Q (i1,j1)) (Q (i2,j2)) = Q (i1+i2,j1+j2)
like image 35
BeMuSeD Avatar answered Sep 18 '22 17:09

BeMuSeD