Skip to content

Instantly share code, notes, and snippets.

@graninas
Last active July 22, 2024 10:18
Show Gist options
  • Save graninas/01e511a1323b342815b941c5af49001b to your computer and use it in GitHub Desktop.
Save graninas/01e511a1323b342815b941c5af49001b to your computer and use it in GitHub Desktop.
Example of a technique for Kana
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FunctionalDependencies #-}
module Main where
import GHC.TypeLits
import Data.Proxy
-- Existential type-level interface making information hiding
data IElement where
ElementWrapper :: a -> IElement
-- Smart constructor
type family MkElement a :: IElement where
MkElement a = ElementWrapper a
-- Specific implementations - elements we want to hide
data Element1Impl
(someData :: Nat)
data Element2Impl
(somethineElse :: Symbol)
data Element3Impl
type Element1 num = MkElement (Element1Impl num)
type Element2 str = MkElement (Element2Impl str)
type Element3 = MkElement Element3Impl
-- Collection has no idea about what IElement is
data Collection
(items :: [IElement])
-- Creation of a specific collection
type MyCollection =
'[ Element1 1
, Element2 "abc"
, Element2 "cde"
, Element3
]
-- Evaluation can access the hidden types
class Eval' verb noun ret
| verb noun -> ret where
eval' :: verb -> Proxy noun -> ret
data ProcessSpecificElement = ProcessSpecificElement
data TraverseElements = TraverseElements
instance
( KnownNat num
) =>
Eval' ProcessSpecificElement (Element1Impl num) (IO ()) where
eval' _ _ = do
print $ "Element1Impl reached"
print $ natVal $ Proxy @num
instance
( KnownSymbol str
) =>
Eval' ProcessSpecificElement (Element2Impl str) (IO ()) where
eval' _ _ = do
print $ "Element2Impl reached"
print $ symbolVal $ Proxy @str
instance
Eval' ProcessSpecificElement Element3Impl (IO ()) where
eval' _ _ = do
print $ "Element3Impl reached"
instance
Eval' TraverseElements (Collection '[]) (IO ()) where
eval' _ _ = pure ()
instance
( Eval' ProcessSpecificElement i (IO ())
, Eval' TraverseElements (Collection items) (IO ())
) =>
Eval'
TraverseElements
(Collection ('ElementWrapper i ': items))
(IO ()) where
eval' _ _ = do
eval' ProcessSpecificElement $ Proxy @i
eval' TraverseElements $ Proxy @(Collection items)
main = eval' TraverseElements $ Proxy @(Collection MyCollection)
-- Output:
-- "Element1Impl reached"
-- 1
-- "Element2Impl reached"
-- "abc"
-- "Element2Impl reached"
-- "cde"
-- "Element3Impl reached"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment