Skip to content

PCF.Combinatory.ScottModelOfPCF

Tom de Jong, 31 May 2019
Updated comments on 21 June 2022.
Added examples at the end on 22 December 2022.

The denotational semantics of PCF based on pointed directed complete posets.

The flag --lossy-unification significantly speeds up the
typechecking of the line ⟦ S {ρ} {Οƒ} {Ο„} βŸ§β‚‘ = Sα΅ˆαΆœα΅–α΅’βŠ₯ ⟦ ρ ⟧ ⟦ Οƒ ⟧ ⟦ Ο„ ⟧ below.
(https://agda.readthedocs.io/en/latest/language/lossy-unification.html)


We consider the combinatory version of PCF here. This development was extended
to PCF with variables and Ξ»-abstraction by Brendan Hart in a final year project
supervised by MartΓ­n EscardΓ³ and myself. Notably, Brendan's extension contains
an Agda formalization of soundness and computational adequacy.

Brendan's code, using a previous version of our library, can be found
here: https://github.com/BrendanHart/Investigating-Properties-of-PCF.

The repository also contains Brendan's report describing the project:
https://github.com/BrendanHart/Investigating-Properties-of-PCF/blob/master/InvestigatingPropertiesOfPCFInAgda.pdf.


{-# OPTIONS --safe --without-K --lossy-unification #-}

open import MLTT.Spartan
open import UF.PropTrunc
open import UF.FunExt
open import UF.Subsingletons

module PCF.Combinatory.ScottModelOfPCF
        (pt : propositional-truncations-exist)
        (fe : Fun-Ext)
        (pe : propext 𝓀₀)
       where

open PropositionalTruncation pt

open import Naturals.Properties
open import UF.Base
open import UF.DiscreteAndSeparated

open import PCF.Combinatory.PCF pt
open import DomainTheory.Basics.Dcpo pt fe 𝓀₀
open import DomainTheory.Basics.Exponential pt fe 𝓀₀
open import DomainTheory.Basics.LeastFixedPoint pt fe 𝓀₀
open import DomainTheory.Basics.Pointed pt fe 𝓀₀

open import PCF.Combinatory.PCFCombinators pt fe 𝓀₀
open IfZeroDenotationalSemantics pe

open import DomainTheory.Lifting.LiftingSet pt fe 𝓀₀ pe

open import Lifting.Construction 𝓀₀ renaming (βŠ₯ to 𝓛βŠ₯)
open import Lifting.Miscelanea 𝓀₀
open import Lifting.Miscelanea-PropExt-FunExt 𝓀₀ pe fe
open import Lifting.Monad 𝓀₀ hiding (ΞΌ)

⟦_⟧ : type β†’ DCPOβŠ₯ {𝓀₁} {𝓀₁}
⟦ ΞΉ ⟧     = 𝓛-DCPOβŠ₯ β„•-is-set
⟦ Οƒ β‡’ Ο„ ⟧ = ⟦ Οƒ ⟧ βŸΉα΅ˆαΆœα΅–α΅’βŠ₯ ⟦ Ο„ ⟧

⟦_βŸ§β‚‘ : {Οƒ : type} (t : PCF Οƒ) β†’ βŸͺ ⟦ Οƒ ⟧ ⟫
⟦ Zero βŸ§β‚‘            = Ξ· zero
⟦ Succ βŸ§β‚‘            = 𝓛̇ succ , 𝓛̇-continuous β„•-is-set β„•-is-set succ
⟦ Pred βŸ§β‚‘            = 𝓛̇ pred , 𝓛̇-continuous β„•-is-set β„•-is-set pred
⟦ ifZero βŸ§β‚‘          = β¦…ifZero⦆
⟦ Fix {Οƒ} βŸ§β‚‘         = ΞΌ ⟦ Οƒ ⟧
⟦ K {Οƒ} {Ο„} βŸ§β‚‘       = Kα΅ˆαΆœα΅–α΅’βŠ₯ ⟦ Οƒ ⟧ ⟦ Ο„ ⟧
⟦ S {ρ} {Οƒ} {Ο„} βŸ§β‚‘   = Sα΅ˆαΆœα΅–α΅’βŠ₯ ⟦ ρ ⟧ ⟦ Οƒ ⟧ ⟦ Ο„ ⟧
⟦ _Β·_ {Οƒ} {Ο„} s t βŸ§β‚‘ = [ ⟦ Οƒ ⟧ ⁻ ,  ⟦ Ο„ ⟧ ⁻ ]⟨ ⟦ s βŸ§β‚‘ ⟩ ⟦ t βŸ§β‚‘


Because Agda is a computational system, we can use it to directly compute the
value of terms in the model. We showcase a few examples illustrating this, as
suggested by Andrej Bauer during my viva on 20 December 2022.


private
 t₁ : PCF ΞΉ
 t₁ = ⌜ 7 ⌝

 recall-the-interpretation-of-ΞΉ : βŸͺ ⟦ ΞΉ ⟧ ⟫ = 𝓛 β„•
 recall-the-interpretation-of-ΞΉ = refl

 ⟦tβ‚βŸ§-is-a-triple-representing-a-partial-element : ⟦ t₁ βŸ§β‚‘
                                                 = πŸ™ , (Ξ» _ β†’ 7) , πŸ™-is-prop
 ⟦tβ‚βŸ§-is-a-triple-representing-a-partial-element = refl

 compute-the-value-of-⟦tβ‚βŸ§ : value ⟦ t₁ βŸ§β‚‘ ⋆ = 7
 compute-the-value-of-⟦tβ‚βŸ§ = refl


 tβ‚‚ : PCF ΞΉ
 tβ‚‚ = Pred Β· (Pred Β· (Succ Β· ⌜ 3 ⌝))

 ⟦tβ‚‚βŸ§-is-a-triple-representing-a-partial-element : ⟦ tβ‚‚ βŸ§β‚‘
                                                 = πŸ™ , (Ξ» _ β†’ 2) , πŸ™-is-prop
 ⟦tβ‚‚βŸ§-is-a-triple-representing-a-partial-element = refl


We let Agda compute the witness (indicated by _) that ⟦ tβ‚‚ βŸ§β‚‘ is total.


 compute-the-value-of-⟦tβ‚‚βŸ§ : value ⟦ tβ‚‚ βŸ§β‚‘ _ = 2
 compute-the-value-of-⟦tβ‚‚βŸ§ = refl


By computational adequacy (see the comments at the top of this file) and the
computation above, the term tβ‚‚ reduces to the numeral ⌜ 2 ⌝ in PCF.

The term t₃ encodes the program [Ξ» x . (if (0 == x) then 2 else (pred 5)) 3].
Notice how the extent of the partial element is no longer given by πŸ™ but, as a
consequence of the constructions in our model, by the product πŸ™ Γ— πŸ™.

We let Agda compute the witness (indicated by _) that the type πŸ™ Γ— πŸ™ is a
proposition.


 t₃ : PCF ΞΉ
 t₃ = ifZero Β· ⌜ 2 ⌝ Β· (Pred Β· ⌜ 5 ⌝) Β· ⌜ 3 ⌝

 ⟦tβ‚ƒβŸ§-is-a-triple-representing-a-partial-element : ⟦ t₃ βŸ§β‚‘
                                                 = (πŸ™ Γ— πŸ™) , (Ξ» _ β†’ 4) , _
 ⟦tβ‚ƒβŸ§-is-a-triple-representing-a-partial-element = refl

 compute-the-value-of-⟦tβ‚ƒβŸ§ : value ⟦ t₃ βŸ§β‚‘ _ = 4
 compute-the-value-of-⟦tβ‚ƒβŸ§ = refl


Next we show two examples using the S and K combinators. We first construct the
identity function I on an arbitrary type Οƒ of PCF using the well-known
definition I = S Β· K Β· K.


 I : {Οƒ : type} β†’ PCF (Οƒ β‡’ Οƒ)
 I {Οƒ} = (S {Οƒ} {Οƒ β‡’ Οƒ} {Οƒ}) Β· K Β· K

 tβ‚„ : PCF ΞΉ
 tβ‚„ = I Β· ⌜ 7 ⌝

 compute-the-value-of-⟦tβ‚„βŸ§ : value ⟦ tβ‚„ βŸ§β‚‘ _ = 7
 compute-the-value-of-⟦tβ‚„βŸ§ = refl

 tβ‚… : PCF ΞΉ
 tβ‚… = (I Β· Succ) Β· ⌜ 11 ⌝

 compute-the-value-of-⟦tβ‚…βŸ§ : value ⟦ tβ‚… βŸ§β‚‘ _ = 12
 compute-the-value-of-⟦tβ‚…βŸ§ = refl


Finally, here are two examples that use the Fix combinator. We explain why we
can't quite compute results in these examples.
Note that this has been updated on 3 March 2026 by Tom de Jong. I am grateful to
Cass Alexandru for questions that led to these updates.


 t₆ : PCF ΞΉ
 t₆ = Fix Β· (K Β· ⌜ 0 ⌝)


Recall that least fixed points are constructed by iterating an endomap and
starting with βŠ₯. In the general case of a π“₯-dcpo we need to lift the type of
natural numbers to a copy in π“₯, which is why we have the type β„•' below. Of
course, since we are dealing with 𝓀₀-dcpos this lifing is unnecessary here, but
it is a consequence of applying the more general constructions.


 open import UF.UniverseEmbedding
 β„•' : 𝓀₀ Μ‡
 β„•' = Lift 𝓀₀ β„•
 _β€² : β„•' β†’ β„•
 _β€² = lower
 β€²_ : β„• β†’ β„•'
 β€²_ = lift 𝓀₀

 t₆-note₁ : is-defined ⟦ t₆ βŸ§β‚‘
            = (βˆƒ n κž‰ β„•' , is-defined (iter ⟦ ΞΉ ⟧ (n β€²) ⟦ K Β· ⌜ 0 ⌝ βŸ§β‚‘))
 t₆-note₁ = refl

 t₆-noteβ‚‚ : (n : β„•') β†’ iter ⟦ ΞΉ ⟧ (succ (n β€²)) ⟦ K Β· ⌜ 0 ⌝ βŸ§β‚‘ = Ξ· 0
 t₆-noteβ‚‚ _ = refl

 t₆-is-defined : is-defined ⟦ t₆ βŸ§β‚‘
 t₆-is-defined = ∣ β€² 1 , ⋆ ∣


The below computation does not work, because extracting the value employs the
fact that we can factor a certain map through the proposition truncation and the
truncation is assumed axiomatically in our development. I would expect it to
work in Cubical Agda though.

 compute-the-value-of-⟦tβ‚†βŸ§ : value ⟦ t₆ βŸ§β‚‘ t₆-is-defined = 0
 compute-the-value-of-⟦tβ‚†βŸ§ = refl

But it is provable of course:


 value-of-⟦tβ‚†βŸ§β‚‘ : value ⟦ t₆ βŸ§β‚‘ t₆-is-defined = 0
 value-of-⟦tβ‚†βŸ§β‚‘ = =-of-values-from-= (eq ⁻¹)
  where
   eq : Ξ· 0 = ⟦ t₆ βŸ§β‚‘
   eq = family-defined-somewhere-sup-= β„•-is-set _ (β€² 1) ⋆


The interpretation of t₇ is equal to βŠ₯, because it is the least fixed point of
the identity on ⟦ ι ⟧, but the issue is that iter is defined by pattern
matching, so while each application iter n is equal to 𝟘, we cannot
definitionally replace it by 𝟘.


 t₇ : PCF ΞΉ
 t₇ = Fix Β· (I {ΞΉ})

 t₇-note₁ : is-defined ⟦ t₇ βŸ§β‚‘
            = (βˆƒ n κž‰ β„•' , is-defined (iter ⟦ ΞΉ ⟧ (n β€²) ⟦ I βŸ§β‚‘))
 t₇-note₁ = refl

 t₇-noteβ‚‚ : (n : β„•) β†’ is-defined (iter ⟦ ΞΉ ⟧ n ⟦ I βŸ§β‚‘) = 𝟘
 t₇-noteβ‚‚ zero     = refl
 t₇-noteβ‚‚ (succ n) = t₇-noteβ‚‚ n

 t₇-note₃ : (n : β„•') β†’ is-defined (iter ⟦ ΞΉ ⟧ (n β€²) ⟦ I βŸ§β‚‘) = 𝟘
 t₇-note₃ n = t₇-noteβ‚‚ (n β€²)


But the following fails:
 t₇-noteβ‚„ : (n : β„•') β†’ is-defined (iter ⟦ ΞΉ ⟧ (n β€²) ⟦ I βŸ§β‚‘) = 𝟘
 t₇-noteβ‚„ n = refl

Therefore we cannot simply compute is-defined ⟦ t₇ βŸ§β‚‘ to be (Ξ£ n κž‰ β„•' , 𝟘),
i.e. β„•' Γ— 𝟘, which is equivalent to 𝟘 of course.


 ⟦tβ‚‡βŸ§-is-not-defined : Β¬ (is-defined ⟦ t₇ βŸ§β‚‘)
 ⟦tβ‚‡βŸ§-is-not-defined = βˆ₯βˆ₯-rec 𝟘-is-prop h
  where
   h : (Ξ£ n κž‰ β„•' , is-defined (iter ⟦ ΞΉ ⟧ (n β€²) ⟦ I βŸ§β‚‘)) β†’ 𝟘
   h (n , d) = Idtofun (t₇-note₃ n) d

 ⟦tβ‚‡βŸ§-is-βŠ₯ : ⟦ t₇ βŸ§β‚‘ = 𝓛βŠ₯
 ⟦tβ‚‡βŸ§-is-βŠ₯ = not-defined-βŠ₯-= ⟦tβ‚‡βŸ§-is-not-defined