Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a GHC extension for enabling overloaded character literals?

I am aware that there is a GHC extension, OverloadedStrings, which allows string literals (delimited by ") to become polymorphic, similar to the built-in behavior for number literals.

My question is: is there a GHC extension that allows single character literals (delimited by ') to become polymorphic in an analogous way?

like image 463
jbweston Avatar asked Nov 16 '19 12:11

jbweston


2 Answers

Not as of GHC 8.8, but you can use the QuasiQuotes extension to get pretty far. Here is an example of a quasiquote which only accepts an ascii character and converts it to its byte representation.

import Language.Haskell.TH
import Language.Haskell.TH.Quote
import Language.Haskell.TH.Syntax

import Data.Word (Word8)
import Data.Char (isAscii)

asciiByte :: QuasiQuoter
asciiByte = QuasiQuoter
  { quoteExp = \str -> case str of
                         [c] | isAscii c -> lift (fromIntegral (fromEnum c) :: Word8)
                         _ -> fail ("asciiByte: expects a single ascii character, got " ++ str) 
  , quotePat = \_ -> fail "asciiByte: only available for expressions"
  , quoteType = \_ -> fail "asciiByte: only available for expressions"
  , quoteDec = \_ -> fail "asciiByte: only available for expressions"
  }

Then, you can use it as:

ghci> [asciiByte|a|]
97
ghci> [asciiByte|é|]

<interactive>:75:12: error:
    • asciiByte: expects a single ascii character, got é
    • In the quasi-quotation: [asciiByte|é|]
ghci> [asciiByte|abc|]

<interactive>:76:12: error:
    • asciiByte: expects a single ascii character, got abc
    • In the quasi-quotation: [asciiByte|abc|]

like image 66
Alec Avatar answered Nov 06 '22 02:11

Alec


No.

Let me answer that with some exploration of the GHC source code:

This is the code in the renamer where OverloadedString take effect:

rnExpr (HsLit x lit@(HsString src s))
  = do { opt_OverloadedStrings <- xoptM LangExt.OverloadedStrings
       ; if opt_OverloadedStrings then
            rnExpr (HsOverLit x (mkHsIsString src s))
         else do {
            ; rnLit lit
            ; return (HsLit x (convertLit lit), emptyFVs) } }

rnExpr (HsLit x lit)
  = do { rnLit lit
       ; return (HsLit x(convertLit lit), emptyFVs) }

rnExpr (HsOverLit x lit)
  = do { ((lit', mb_neg), fvs) <- rnOverLit lit -- See Note [Negative zero]
       ; case mb_neg of
              Nothing -> return (HsOverLit x lit', fvs)
              Just neg -> return (HsApp x (noLoc neg) (noLoc (HsOverLit x lit'))
                                 , fvs ) }

(https://github.com/ghc/ghc/blob/c2991f16cb6f5b4e7cff46a394dda4247d973f44/compiler/rename/RnExpr.hs#L152)

You see that there is special handling for strings, but not for other forms of literals. The last clause is for literals that are overloaded according to the original Haskell standard, as we can see in these lines from the parser:

        | literal                       { ECP $ mkHsLitPV $! $1 }
-- This will enable overloaded strings permanently.  Normally the renamer turns HsString
-- into HsOverLit when -foverloaded-strings is on.
--      | STRING    { sL (getLoc $1) (HsOverLit $! mkHsIsString (getSTRINGs $1)
--                                       (getSTRING $1) noExt) }
        | INTEGER   { ECP $ mkHsOverLitPV (sL1 $1 $ mkHsIntegral   (getINTEGER  $1)) }
        | RATIONAL  { ECP $ mkHsOverLitPV (sL1 $1 $ mkHsFractional (getRATIONAL $1)) }

(https://github.com/ghc/ghc/blob/c2991f16cb6f5b4e7cff46a394dda4247d973f44/compiler/parser/Parser.y#L2793)

literal :: { Located (HsLit GhcPs) }
        : CHAR              { sL1 $1 $ HsChar       (getCHARs $1) $ getCHAR $1 }
        | STRING            { sL1 $1 $ HsString     (getSTRINGs $1)
                                                    $ getSTRING $1 }
        | PRIMINTEGER       { sL1 $1 $ HsIntPrim    (getPRIMINTEGERs $1)
                                                    $ getPRIMINTEGER $1 }
        | PRIMWORD          { sL1 $1 $ HsWordPrim   (getPRIMWORDs $1)
                                                    $ getPRIMWORD $1 }
        | PRIMCHAR          { sL1 $1 $ HsCharPrim   (getPRIMCHARs $1)
                                                    $ getPRIMCHAR $1 }
        | PRIMSTRING        { sL1 $1 $ HsStringPrim (getPRIMSTRINGs $1)
                                                    $ getPRIMSTRING $1 }
        | PRIMFLOAT         { sL1 $1 $ HsFloatPrim  noExt $ getPRIMFLOAT $1 }
        | PRIMDOUBLE        { sL1 $1 $ HsDoublePrim noExt $ getPRIMDOUBLE $1 }

(https://github.com/ghc/ghc/blob/c2991f16cb6f5b4e7cff46a394dda4247d973f44/compiler/parser/Parser.y#L3708)

like image 25
Joachim Breitner Avatar answered Nov 06 '22 03:11

Joachim Breitner