packages feed

poly-rec-0.7.0.4: src/Data/GenRec/Unordered.hs

{-|
Module      : Data.GenRecord.Unordered
Description : polykinded extensible record library
Copyright   : (c) Juan García Garland (2023)
License     : GPL
Maintainer  : jpgarcia@fing.edu.uy
Stability   : experimental
Portability : POSIX

Records where labels are names (|Symbol|s) and preserve the insertion
order (hence they are not ordered alphanumerically).

We did not reuse Generic record constructs from |Data.GenRec| and
built new definitions instead. This module is intended to mimmick
ordinary Haskell records, while being extensive.

One quirk of the definitions in this module is that we admit repeated
labels. Ordinary lookup will get the first occurrence, but we build
different kind of projections to get all values under a label. This
development was first intended to represent a collection of registers
with names and values, hence the field name.

-}

{-# OPTIONS_GHC -fno-warn-missing-methods #-}

{-# LANGUAGE DataKinds,
             TypeOperators,
             PolyKinds,
             GADTs,
             RankNTypes,
             StandaloneDeriving,
             FlexibleInstances,
             FlexibleContexts,
             ConstraintKinds,
             MultiParamTypeClasses,
             FunctionalDependencies,
             UndecidableInstances,
             ScopedTypeVariables,
             TypeFamilies,
             InstanceSigs,
             AllowAmbiguousTypes,
             TypeApplications,
             PatternSynonyms,
             DataKinds, PolyKinds
#-}

module Data.GenRec.Unordered where
import Data.Label
import GHC.TypeLits (Symbol, symbolVal, KnownSymbol)
import Data.Kind
import Data.Proxy
import Data.Type.Require

import Data.GenRec (UnWrap)

data Reg (l :: Symbol) v where
  Reg :: (KnownSymbol l) => v -> Reg l v

instance
  Show v
  =>
  Show (Reg l v) where
  show (Reg v) = symbolVal (Proxy @l) ++ " : " ++ show v

infixr 5 :<
data Record (r :: [(Symbol, Type)]) :: Type where
  Empty :: Record '[] -- ^ empty record
  (:<) :: Reg l v -> Record r -> Record ('( l, v) ': r) -- ^

instance
  Show (Record '[]) where
  show Empty = "{}"

instance
  (Show v)
  =>
  Show (Record '[ '(l,v)]) where
  show (reg :< _) = "{ " ++ show reg ++ "}"
  
instance
  ( Show v
  , Show (Record (lv ': r)))
  =>
  Show (Record ( '(l, v) ': lv ': r)) where
  show (reg :< r) =  "{" ++ show reg ++ ", " ++ tail (show r)

ex1 =  Reg @"x" 3.0
    :< Reg @"y" 2.1
    :< Reg @"z" ()
    :< Reg @"t" 0
    :< Empty

ex2 =  Reg @"header" ()
    :< Reg @"val" 2.1
    :< Reg @"val" 1.1
    :< Reg @"val" 0.0
    :< Reg @"footer" "END"
    :< Empty

type instance UnWrap (Record l) = l

data OpSnoc (l :: Symbol) (v :: Type) (r :: [(Symbol, Type)]) where
  OpSnoc :: Reg l v -> Record r -> OpSnoc l v r

instance
  Require (OpSnoc l v '[]) ctx where
  type ReqR (OpSnoc l v '[])
    = Record '[ '(l, v)]
  req ctx (OpSnoc lv _) = lv :< Empty


instance
  RequireR (OpSnoc l v r) (Record (UnWrap (ReqR (OpSnoc l v r)))) ctx
  =>
  Require (OpSnoc l v ( '(l',v') ': r)) ctx where
  type ReqR (OpSnoc l v ( '(l',v') ': r)) =
    Record ( '(l',v') ': UnWrap @Symbol @Type (ReqR (OpSnoc l v r)))
  req ctx (OpSnoc lv (lv' :< r)) = lv' :< req ctx (OpSnoc lv r) 

r >: lv = req (Proxy @'[]) (OpSnoc lv r)