-
Notifications
You must be signed in to change notification settings - Fork 87
3 classes for Symbol #511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
3 classes for Symbol #511
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,15 +3,29 @@ module Data.TypeLits( | |
| Nat, | ||
| KnownNat(..), | ||
| KnownSymbol(..), | ||
| SymbolEq, | ||
| ConcatSymbol, | ||
| HeadSymbol | ||
| ) where | ||
| import qualified Prelude() | ||
| import Primitives | ||
| import Data.Char_Type | ||
| import Data.Integer | ||
| import {-# SOURCE #-} Data.Typeable | ||
|
|
||
| -- Special classes solved by the typechecker. | ||
| -- An instance of one of these classes would be useless. | ||
|
|
||
| class KnownNat (n :: Nat) where | ||
| natVal :: forall (proxy :: Nat -> Type) . proxy n -> Integer | ||
|
|
||
| class KnownSymbol (s :: Symbol) where | ||
| symbolVal :: forall (proxy :: Symbol -> Type) . proxy s -> String | ||
|
|
||
| -- Tests two litteral Symbols equality and returns "True" or "False". | ||
| class SymbolEq (s :: Symbol) (t :: Symbol) (b :: Symbol) | s t -> b | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this what GHC does? I try to be compatible.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Title: GHC-compatible MotivationMicroHs already supports In GHC, type family AppendSymbol (m :: Symbol) (n :: Symbol) :: SymbolCrucially, it is used inline, in type position, and reduces during f :: Proxy (AppendSymbol "foo" "bar") -> ...What I tried as a workaroundWithout class ConcatSymbol (s :: Symbol) (t :: Symbol) (st :: Symbol)
| s t -> st, st t -> s, st s -> tsolveConcatSymbol :: SolveOne
solveConcatSymbol loc iCls [s, t, st] = ...
-- pattern-matches on ELit (LStr ...) literals, computes the concatenation
-- or the missing side (given the other two), unifies via Improve, or
-- errors out on a literal mismatch
solveConcatSymbol loc iCls ts = solveInst loc iCls tsThis works well as a relation: it can check, complete, or reject a triple Where this workaround falls shortA class-based relation can never be GHC-compatible in the way Proposed scopeGiven MicroHs's stated goal of GHC compatibility, and that a similarly
This would be intentionally much narrower than general If this is out of scope for MicroHs's design goals, the class/fundep Happy to share the full prototype ( |
||
|
|
||
| class ConcatSymbol (s :: Symbol) (t :: Symbol) (st :: Symbol) | s t -> st, st s -> t, st t -> s | ||
|
|
||
| class HeadSymbol (h :: Symbol) (t :: Symbol) (s :: Symbol) | ||
| | h t -> s, s -> h t | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| module ConcatSymbol where | ||
| import Data.Proxy | ||
| import Data.TypeLits | ||
|
|
||
| testSuccess :: ConcatSymbol "ab" "cd" "abcd" => Bool | ||
| testSuccess = True | ||
|
|
||
| testConcat :: ConcatSymbol s1 s2 s3 => Proxy s1 -> Proxy s2 -> Proxy s3 | ||
| testConcat _ _ = Proxy | ||
|
|
||
| testPrefix :: ConcatSymbol s1 s2 s3 => Proxy s3 -> Proxy s2 -> Proxy s1 | ||
| testPrefix _ _ = Proxy | ||
|
|
||
| testSuffix :: ConcatSymbol s1 s2 s3 => Proxy s3 -> Proxy s1 -> Proxy s2 | ||
| testSuffix _ _ = Proxy | ||
|
|
||
| testChain :: | ||
| ( ConcatSymbol s1 s2 s3 | ||
| , ConcatSymbol s3 s4 s7) | ||
| => Proxy s1 -> Proxy s2 -> Proxy s4 -> Proxy s7 | ||
| testChain _ _ _ = Proxy | ||
|
|
||
| main = do | ||
| putStrLn $ show testSuccess | ||
| putStrLn $ symbolVal $ testConcat (Proxy :: Proxy "ab") (Proxy :: Proxy "cd") | ||
| putStrLn $ symbolVal $ testPrefix (Proxy :: Proxy "abcd") (Proxy :: Proxy "cd") | ||
| putStrLn $ symbolVal $ testSuffix (Proxy :: Proxy "abcd") (Proxy :: Proxy "ab") | ||
| putStrLn $ symbolVal $ | ||
| testChain (Proxy :: Proxy "ab") (Proxy :: Proxy "cd") (Proxy :: Proxy "ef") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| True | ||
| abcd | ||
| ab | ||
| cd | ||
| abcdef |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| module HeadSymbolEq where | ||
|
|
||
| import Data.Proxy | ||
| import Data.TypeLits (Symbol, KnownSymbol, symbolVal, ConcatSymbol, HeadSymbol, SymbolEq) | ||
|
|
||
| -------------------------------------------------------------------------------- | ||
| -- Specifier : Lit wraps a Symbol. | ||
| -------------------------------------------------------------------------------- | ||
|
|
||
| data D -- digit | ||
| data S -- string | ||
| data Lit (lit :: Symbol) | ||
|
|
||
| class Specifier s | ||
| instance Specifier D | ||
| instance Specifier S | ||
| instance (KnownSymbol lit) => Specifier (Lit lit) | ||
|
|
||
| -------------------------------------------------------------------------------- | ||
| -- FList : lists the formats. | ||
| -------------------------------------------------------------------------------- | ||
|
|
||
| data FNil | ||
| data FCons s fl | ||
|
|
||
| class FList fl | ||
| instance FList FNil | ||
| instance (Specifier s, FList fl) => FList (FCons s fl) | ||
|
|
||
| -------------------------------------------------------------------------------- | ||
| -- FormatF : splits the %d / %s formats | ||
| -------------------------------------------------------------------------------- | ||
|
|
||
| class (FList format) => FormatF format fun | format -> fun where | ||
| formatF :: Proxy format -> String -> fun | ||
|
|
||
| instance FormatF FNil String where | ||
| formatF _ = id | ||
|
|
||
| instance (FormatF rest fun) | ||
| => FormatF (FCons D rest) (Int -> fun) where | ||
| formatF _ str = \i -> formatF (Proxy :: Proxy rest) (str ++ show i) | ||
|
|
||
| instance (FormatF rest fun) | ||
| => FormatF (FCons S rest) (String -> fun) where | ||
| formatF _ str = \s -> formatF (Proxy :: Proxy rest) (str ++ s) | ||
|
|
||
| instance (KnownSymbol lit, FormatF rest fun) | ||
| => FormatF (FCons (Lit lit) rest) fun where | ||
| formatF _ str | ||
| = formatF (Proxy :: Proxy rest) (str ++ symbolVal (Proxy :: Proxy lit)) | ||
|
|
||
| -------------------------------------------------------------------------------- | ||
| -- MatchFmt | ||
| -------------------------------------------------------------------------------- | ||
|
|
||
| class (Specifier out) => MatchFmt (head :: Symbol) out | head -> out | ||
| instance MatchFmt "d" D | ||
| instance MatchFmt "s" S | ||
|
|
||
| -------------------------------------------------------------------------------- | ||
| -- Parse : uses SymbolEq -> "True"/"False" to avoid instance overlappings. | ||
| -------------------------------------------------------------------------------- | ||
|
|
||
| class (FList format) => Parse (string :: Symbol) format | string -> format | ||
| instance (SymbolEq string "" isEmpty, ParseC isEmpty string format) | ||
| => Parse string format | ||
|
|
||
| class (FList out) => ParseC (isEmpty :: Symbol) (string :: Symbol) out | isEmpty string -> out | ||
|
|
||
| instance ParseC "True" string (FCons (Lit "") FNil) | ||
|
|
||
| instance (HeadSymbol h t string, Match h t out) | ||
| => ParseC "False" string out | ||
|
|
||
| -------------------------------------------------------------------------------- | ||
| -- Match : uses SymbolEq also. | ||
| -------------------------------------------------------------------------------- | ||
|
|
||
| class (FList out) => Match (h :: Symbol) (t :: Symbol) out | h t -> out | ||
| instance (SymbolEq h "%" isPct, MatchC isPct h t out) | ||
| => Match h t out | ||
|
|
||
| class (FList out) => MatchC (isPct :: Symbol) (h :: Symbol) (t :: Symbol) out | ||
| | isPct h t -> out | ||
|
|
||
| -- '%' : on decompose t pour recuperer le caractere de specification (h2) | ||
| -- et le reste (t2) | ||
| instance (HeadSymbol h2 t2 t, MatchFmt h2 spec, Parse t2 rest) | ||
| => MatchC "True" h t (FCons (Lit "") (FCons spec rest)) | ||
|
|
||
| -- caractere ordinaire : accumule via ConcatSymbol (h prefixe acc) | ||
| instance (FList r, KnownSymbol acc', ConcatSymbol h acc acc', Parse t (FCons (Lit acc) r)) | ||
| => MatchC "False" h t (FCons (Lit acc') r) | ||
|
|
||
| -------------------------------------------------------------------------------- | ||
| -- Format | ||
| -------------------------------------------------------------------------------- | ||
|
|
||
| class Format (string :: Symbol) fun | string -> fun where | ||
| format :: Proxy string -> fun | ||
|
|
||
| instance (Parse string format, FormatF format fun) | ||
| => Format string fun where | ||
| format _ = formatF (Proxy :: Proxy format) "" | ||
|
|
||
| -------------------------------------------------------------------------------- | ||
| -- Exemple | ||
| -------------------------------------------------------------------------------- | ||
|
|
||
| main :: IO () | ||
| main = do | ||
| let formatted = format (Proxy :: Proxy "Hi %s! You are %d") "Bill" 12 | ||
| putStrLn formatted -- "Hi Bill! You are 12" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Hi Bill! You are 12 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these are not exactly GHC compatible, I'd like them to have names that are not the same as GHC's. I will eventually implement GHC compatible type level operations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GHC's versions are definitely easier to use than these. Having a compatible version (or a type family implementation) in MicroHs would be preferable and I don't mind at all closing this PR to limit confusion.