Skip to content

LinearTypes: Spurious multiplicity error when wrapping an empty-case lambda in a data/newtype constructor

Summary

When using -XLinearTypes and -XEmptyCase, a lambda that eliminates Void via case void of {} type-checks correctly when used directly as the body of a linear function, but is rejected with a multiplicity mismatch when the same lambda is passed as an argument to a data or newtype constructor.

Steps to reproduce

{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE LinearTypes #-}

import Data.Void

ok1 :: a %1 -> Void %1 -> ()
ok1 a void = case void of {}

ok2 :: a %1 -> Void %1 -> ()
ok2 a = \void -> case void of {}

data DT = DT (Void %1 -> ())

newtype NT = NT (Void %1 -> ())

ng1 :: a %1 -> DT
ng1 a = DT (\void -> case void of {})

ng2 :: a %1 -> NT
ng2 a = NT (\void -> case void of {})

When I load the code above, I get the following error:

LinearHaskellMystery.hs:17:5: error: [GHC-18872]
    • Couldn't match type ‘Many’ with ‘One’
        arising from multiplicity of ‘a’
    • In an equation for ‘ng1’: ng1 a = DT (\ void -> case void of {})
   |
17 | ng1 a = DT (\void -> case void of {})
   |     ^

LinearHaskellMystery.hs:20:5: error: [GHC-18872]
    • Couldn't match type ‘Many’ with ‘One’
        arising from multiplicity of ‘a’
    • In an equation for ‘ng2’: ng2 a = NT (\ void -> case void of {})
   |
20 | ng2 a = NT (\void -> case void of {})
   |     ^

Expected behavior

All four definitions (ok1, ok2, ng1, ng2) should be accepted. In all cases, a is bound linearly but the body is an empty case analysis on Void. Since there are zero branches, it vacuously satisfies the linearity requirement (there is no branch where a is not consumed exactly once, because there are no branches at all).

Environment

  • GHC version used: 9.14.1

Optional:

  • Operating System: macOS Sequoia 15.7.4
  • System Architecture: Apple M4
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information