So if I understand it right, we have different denotations for the different function types (ignoring multiplicities):
⟦ FUN @_ @Type A B ⟧ = ⟦ A ⟧ → ⟦ B ⟧
⟦ FUN @_ @UnliftedType A B ⟧ = ⟦ A ⟧ → ⟦ B ⟧ ∪ {⊥}
If the codomain is Type
then the ⊥ is part of ⟦ B ⟧
.
So essentially GHC automatically “levitates” unlifted codomains of functions.
Edit: And let
's of unlifted variables now become like binds in the partiality monad:
⟦ let x :: Int# = M in N ⟧ = if ⟦ M ⟧ = ⊥ then ⊥ else ⟦ N[ x := ⟦ M ⟧ ] ⟧
Edit: But this does raise questions if the body of the let is also unlifted, e.g. N :: Int#
, because then you’d expect the type of the whole let expression to be Int#
as well. And the denotation of that is ⟦ Int# ⟧ = {-2^31..2^31 - 1}
, but that does not include ⊥, so where would that go?