是否有用于启用重载字符文字的 GHC 扩展?

Is there a GHC extension for enabling overloaded character literals?

我知道有一个 GHC 扩展 OverloadedStrings,它允许 字符串文字 (由 " 分隔)变成多态的,类似于数字文字的内置行为。

我的问题是:是否有 GHC 扩展允许 单字符文字 (由 ' 分隔)以类似的方式变成多态的?

没有

让我通过对 GHC 源代码的一些探索来回答这个问题:

这是重命名器中 OverloadedString 生效的代码:

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)

您看到对字符串有特殊处理,但对其他形式的文字没有。最后一个子句用于根据原始 Haskell 标准重载的文字,正如我们在解析器的这些行中看到的那样:

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

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

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

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

从 GHC 8.8 开始还没有,但您可以使用 QuasiQuotes 扩展来获得更多。这是一个仅接受 ascii 字符并将其转换为其字节表示的准引号示例。

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"
  }

然后,您可以将其用作:

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|]