简体   繁体   中英

Replacing self built Naturals with GHC type level literals

I wrote some code that takes a Heterogeneous List and indexes it.

{-# Language GADTs, FunctionalDependencies, MultiParamTypeClasses, KindSignatures, DataKinds, TypeOperators, FlexibleInstances, UndecidableInstances #-}

import Data.Kind

data Nat = Z | S Nat

data Natural a where
  Zero :: Natural 'Z
  Succ :: Natural a -> Natural ('S a)

data HList a where
  EmptyList :: HList '[]
  Cons :: a -> HList b -> HList (a ': b)

class IndexType (n :: Nat) (a :: [Type]) (b :: Type) | n a -> b where
  index :: (Natural n) -> (HList a) -> b

instance IndexType 'Z (a ': b) a where
  index _ (Cons a _) = a

instance IndexType a b c => IndexType ('S a) (d ': b) c where
  index (Succ a) (Cons _ b) = index a b

To do this I implemented my own Nat and Natural types. The Nat exists solely to elevate to the Kind level and Natural exists to fulfill the kind Nat -> Type .

Now I would prefer to use GHC.TypeLits ' Nat kind rather than my own however when I try to translate my code over I start to hit a wall in terms of understanding.

I want to build my IndexType class and the declaration line doesn't change any

class IndexType (n :: Nat) (a :: [Type]) (b :: Type) | n a -> b where

Since GHC.TypeLits also has its own Nat kind. However GHC.TypeLits doesn't have a replacement for Natural that I see, namely I lack something of the kind Nat -> Type . Now I could build an equivalent

data Natural a = Natural

But this is essentially equivalent the Proxy type so I could just use that instead.

{-# Language GADTs, FunctionalDependencies, MultiParamTypeClasses, KindSignatures, DataKinds, TypeOperators, FlexibleInstances, UndecidableInstances #-}

import Data.Kind
import GHC.TypeLits
import Data.Proxy

data HList a where
  EmptyList :: HList '[]
  Cons :: a -> HList b -> HList (a ': b)

class IndexType (n :: Nat) (a :: [Type]) (b :: Type) | n a -> b where
  index :: (Proxy n) -> (HList a) -> b

Now the first instance of the IndexType class is easy enough:

instance IndexType 0 (a ': b) a where
  index _ (Cons a _) = a

However the second one starts to puzzle me. The first line seems as if it would be

instance IndexType a b c => IndexType (1 + a) (d ': b) c where

However on the second line I don't know how to replace the Succ in the original code. The data constructor for Proxy is Proxy so I suppose it must use that constructor so I must write something like:

  index Proxy (Cons _ b) = index a b

But now I'm pulling the definition of a out of thin air. I suppose it has to be another Proxy since index takes a Proxy , but I don't know how to force it to be the correct type.

How about this?

class IndexType (n :: Nat) (a :: [Type]) (c :: Type) | n a -> c where           
  index ::  (Proxy n) -> (HList a) -> c                                         
instance IndexType 0  (a ': b) a where                                          
  index _ (Cons a _) = a                                                        
instance {-# OVERLAPS #-} (IndexType (a-1) b c) => IndexType a (d ': b) c where 
  index _ (Cons _ b) = index (Proxy @(a-1)) b                                   

This will use some extra extensions including ScopedTypeVariables and TypeApplications . PoC (tested on GHC 8.2.2):

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module Foo where
import Data.Kind
import GHC.TypeLits
import Data.Proxy

data HList a where
  EmptyList :: HList '[]
  Cons :: a -> HList b -> HList (a ': b)

class IndexType (n :: Nat) (a :: [Type]) (c :: Type) | n a -> c where
  index ::  (Proxy n) -> (HList a) -> c
instance IndexType 0  (a ': b) a where
  index _ (Cons a _) = a
instance {-# OVERLAPS #-} (IndexType (a-1) b c) => IndexType a (d ': b) c where
  index _ (Cons _ b) = index (Proxy @(a-1)) b

list :: HList '[Int, Bool]
list = Cons (5 :: Int) (Cons True EmptyList)
int :: Int
int = index (Proxy @0) list
bool :: Bool
bool = index (Proxy @1) list

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM