An example of a simple dependent type in Haskell for dummies. How are they useful in practice at Haskell? Why should I care about dependent types?

Currently, I hear a lot about dependent types, and I heard that DataKinds is somehow related to dependent typing (but I'm not sure about that ... just heard about it in Haskell Meetup).

Can anyone illustrate with a super simple Haskell example what dependency typing is and what it is useful for?

It is written on wikipedia that dependent types can help prevent errors. Could you give a simple example of how dependent types in Haskell can prevent errors?

Something I could start using in five minutes to prevent errors in my Haskell code?

Dependent types are basically functions from values ​​to types, how can this be used in practice? Why is it good?

+4
source share
4 answers

Late party, this answer is mostly shameless.

Sam Lindley and I wrote an article about Hasochism , about the pleasure and pain of obsessive programming at Haskell. This gives many examples of what is now possible in Haskell, and draws comparison points (both favorable and not) with the generation of Agda / Idris in languages ​​with a typical type.

, , Sam. (, mergesort), , : , ( , ) .

(, ). , . ( ) :

  • if b then t else e = > if b then e else t
  • if b then t else e = > t
  • if b then t else e = > e

b, , ( ) , : , b t, e, .

- . ,

f :: forall a. r[a] -> s[a] -> t[a]

a: a, , - , . , (, ) ().

GADT, , . n; , ; , n . , , , -, , . , , , .

, . 1 ( , , s -> t t -to-the- s) , : if 0, ; 1, -; 2, ; , . - , , , . : . , , , , , : . .

+9

, , ().

cons :: a -> List a n -> List a (n+1)

n - . , .

head ( ),

 head :: n > 0 => List a n -> a

,

to3uple :: List a 3 -> (a,a,a)

, head , , .

, :

 head (a `cons` l)

-

 if null list
    then ...
    else (head list)

head, else , .

Haskell , , , DataKind, int , List a b List Int 1. (b - phantom, ).

, Haskell.

{-# LANGUAGE DataKinds, KindSignatures, TypeFamilies, TypeOperators #-}

import GHC.TypeLits


data List a (n:: Nat) = List [a] deriving Show

cons :: a -> List a n -> List a (n + 1)
cons x (List xs) = List (x:xs)

singleton :: a -> List a 1
singleton x = List [x]

data NonEmpty
data EmptyList

type family ListLength a where
  ListLength (List a 0) =  EmptyList
  ListLength (List a n) = NonEmpty

head' :: (ListLength (List a n) ~ NonEmpty) => List a n -> a
head' (List xs) = head xs

tail' :: (ListLength (List a n) ~ NonEmpty) => List a n -> List a (n-1)
tail' (List xs) = List (tail xs)

list = singleton "a"

head' list -- return "a"

head' (tail' list)

Couldn't match type ‘EmptyList’ with ‘NonEmpty’
Expected type: NonEmpty
  Actual type: ListLength (List [Char] 0)
In the expression: head' (tail' list)
In an equation for ‘it’: it = head' (tail' list)
+4

@mb14, .

-, DataKinds, GADTs KindSignatures, :

{-# LANGUAGE DataKinds      #-}
{-# LANGUAGE GADTS          #-}
{-# LANGUAGE KindSignatures #-}

Nat Vector :

data Nat :: * where
    Z :: Nat
    S :: Nat -> Nat

data Vector :: Nat -> * -> * where
    Nil   :: Vector Z a
    (:-:) :: a -> Vector n a -> Vector (S n) a

voila, , .

head tail:

head' :: Vector (S n) a -> a
head' (a :-: _) = a
-- The other constructor, Nil, doesn't apply here because of the type signature!

tail' :: Vector (S n) a -> Vector n a
tail (_ :-: xs) = xs
-- Ditto here.

, , .

, Haskell , . , Idris, Haskell, , .

+4

machines , . , , , . GADT, .

Step k o r = ...
           | forall t . Await (t -> r) (k t) r

The machine provides a type request k tfor some undefined type tand a function to process the result. By the model corresponding to the request, the runner of the machine finds out what type he should put on the machine. The machine response handler should not verify that it has received the correct answer.

+3
source

Source: https://habr.com/ru/post/1609169/


All Articles