External Publication
Visit Post

Why no exposed `NullAddr#` literal?

Haskell Community [Unofficial] May 27, 2026
Source

jaror:

TIL pattern synonyms can be unlifted. But then a bidirectional pattern should work:

pattern MyNullAddr# :: MyAddr#
pattern MyNullAddr# <- ((\(WrapAddr# x) -> eqAddr# nullAddr# x) -> 1#) where
  MyNullAddr# = WrapAddr# nullAddr#

Awesome, thanks! This does spark joy.

tomjaguarpaw:

I’m not sure it is fair, or at least there could in principle be an exception for values, and a newtype constructor applied to a value would be value. Maybe that adds too much extra complexity to be worth it.

My impression is that on the frontend (so more superficially than @jaror’s work above, but also more programmer-visibly) and with the exception of the nullAddr# issue, a statically-known fully evaluated unlifted value should be able to be built up from literals and constructors, hence should be bindable as an implicitly bidirectional unlifted pattern synonym. Language.Haskell.TH.Lit already represents literals of types Int#, Word#, Char#, Float#, and Double# (and hopefully once -XExtendedLiterals is fully supported by TemplateHaskell, also Int8#, Int16#, Int32#, Int64#, Word8#, Word16#, Word32#, and Word64#—maybe even eventually SIMD literals, although those aren’t currently supported by -XExtendedLiterals either), so in cases where the literal value is the result of a nontrivial computation one can do something like

{-# LANGUAGE Haskell2010 #-}

module MyInt.TH where

{- | Here because of the stage restriction -}
complicated :: Integer -> Integer
complicated = \ n -> product [ 1 .. n ]

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

{-# LANGUAGE Haskell2010
  , GADTSyntax
  , MagicHash
  , PatternSynonyms
  , UnliftedNewtypes
  , TemplateHaskell
#-}

module MyInt where

import GHC.Exts
import Language.Haskell.TH
import MyInt.TH

newtype MyInt# where
    WrapInt# :: Int# -> MyInt#

data MyInt where
    MyI# :: Int# -> MyInt

{- |  Binds 'WrapInt# 2432902008176640000#' as a pattern -}
$(pure $
    let nm = mkName "Fac20Int#"
        n = complicated 20
    in  [ PatSynD
            ( nm )
            ( PrefixPatSyn [ ] )
            ( ImplBidir )
            ( ConP
                ( 'WrapInt# )
                [ ]
                [ LitP ( IntPrimL n ) ] )
        , PatSynSigD
            ( nm )
            ( ConT ''MyInt# ) ]
  )

{- |  Binds 'MyI# 2432902008176640000#' as a value -}
fac20Int :: MyInt
fac20Int = $(pure $
    let n = complicated 20
    in  AppE
          ( ConE 'MyI# )
          ( LitE ( IntPrimL n ) )
  )

Discussion in the ATmosphere

Loading comments...