Source code on Github
open import Prelude.Init; open SetAsType
open L.Mem
open import Prelude.General
open import Prelude.DecEq
open import Prelude.Decidable
open import Prelude.Setoid
open import Prelude.InferenceRules
open import Prelude.InfEnumerable

module Nominal.Fun (Atom : Type)  _ : DecEq Atom  where

open import Nominal.Swap Atom
open import Nominal.Support Atom

module _ {A : Type } {B : Type ℓ′}  _ : Swap A   _ : Swap B  where

  open ≈-Reasoning

  instance
    Swap-Fun : Swap (A  B)
    Swap-Fun .swap a b f = swap a b  f  swap a b

    Setoid-Fun :  ISetoid B   ISetoid (A  B)
    Setoid-Fun = λ where
      .relℓ   ⊔ₗ relℓ {A = B}
      ._≈_  f g   x  f x  g x
      -- ._≈_  f g → ∀ x y → x ≈ y → f x ≈ g y

    SetoidLaws-Fun :
       _ : ISetoid B    SetoidLaws B 
       SetoidLaws (A  B)
    SetoidLaws-Fun .isEquivalence = record
      { refl  = λ {f} x  ≈-refl
      ; sym   = λ f∼g x  ≈-sym (f∼g x)
      ; trans = λ f∼g g∼h x  ≈-trans (f∼g x) (g∼h x)
      }

    SwapLaws-Fun :
       _ : ISetoid A   _ : SetoidLaws A   _ : CongSetoid A   _ : SwapLaws A 
       _ : ISetoid B   _ : SetoidLaws B   _ : SwapLaws B 
       SwapLaws (A  B)
    SwapLaws-Fun .cong-swap {f}{g}{a}{b} f≗g x =
    -- ∀ {f g : A → B} → x ≈ y → ⦅ 𝕒 ↔ 𝕓 ⦆ f ≈ ⦅ 𝕒 ↔ 𝕓 ⦆ g
      cong-swap (f≗g _)
    SwapLaws-Fun .swap-id {a}{f} x =
    -- ∀ {f : A → B} → ⦅ 𝕒 ↔ 𝕒 ⦆ f ≈ f
      begin
         a  a  (f ( a  a  x))
      ≈⟨ swap-id 
        f ( a  a  x)
      ≈⟨ ≈-cong f swap-id 
        f x
      
    SwapLaws-Fun .swap-rev {a}{b}{f} x =
    -- ∀ {f : A → B} → ⦅ 𝕒 ↔ 𝕓 ⦆ f ≈ ⦅ 𝕓 ↔ 𝕒 ⦆ f
      begin
        ( a  b  f) x
      ≡⟨⟩
         a  b  (f $  a  b  x)
      ≈⟨ cong-swap $ ≈-cong f swap-rev 
         a  b  (f $  b  a  x)
      ≈⟨ swap-rev 
         b  a  (f $  b  a  x)
      ≡⟨⟩
        ( b  a  f) x
      
    SwapLaws-Fun .swap-sym {a}{b}{f} x =
    -- ∀ {f : A → B} → ⦅ 𝕒 ↔ 𝕓 ⦆ ⦅ 𝕓 ↔ 𝕒 ⦆ f ≈ f
      begin
        ( a  b   b  a  f) x
      ≡⟨⟩
         a  b   b  a  (f $  b  a   a  b  x)
      ≈⟨ cong-swap $ cong-swap $ ≈-cong f swap-sym 
         a  b   b  a  (f x)
      ≈⟨ swap-sym 
        f x
      
    SwapLaws-Fun .swap-swap {𝕒 = a}{b}{c}{d}{f} x =
    -- ∀ {f : A → B} → ⦅ 𝕒 ↔ 𝕓 ⦆ ⦅ 𝕔 ↔ 𝕕 ⦆ f
    --               ≈ ⦅ ⦅ 𝕒 ↔ 𝕓 ⦆ 𝕔 ↔ ⦅ 𝕒 ↔ 𝕓 ⦆ 𝕕 ⦆ ⦅ 𝕒 ↔ 𝕓 ⦆ f
      begin
        ( a  b   c  d  f) x
      ≡⟨⟩
         a  b   c  d  (f $  c  d   a  b  x)
      ≈⟨ swap-swap 
          a  b  c   a  b  d   a  b 
          (f $  c  d   a  b  x)
      --                ↑ NB: note the change of ordering on swappings
      ≈⟨ cong-swap $ cong-swap $ ≈-cong f
       $ begin
            c  d   a  b  x
         ≡˘⟨ cong     c     a  b  x) swap-sym′ 
            c   a  b   a  b  d   a  b  x
         ≡˘⟨ cong        a  b   a  b  d   a  b  x) swap-sym′ 
             a  b   a  b  c   a  b   a  b  d   a  b  x
         ≈˘⟨ swap-swap 
            a  b    a  b  c   a  b  d  x
         
       
          a  b  c   a  b  d   a  b 
          (f $  a  b    a  b  c   a  b  d  x)
      ≡⟨⟩
        (  a  b  c   a  b  d   a  b  f) x
      

  -- NB: swapping takes the conjugation action on functions
  module _
     _ : ISetoid A   _ : SetoidLaws A   _ : SwapLaws A   _ : CongSetoid A 
     _ : ISetoid B   _ : SetoidLaws B   _ : SwapLaws B 
    where
    conj :  {𝕒 𝕓 : Atom} (f : A  B) (x : A) 
      (swap 𝕒 𝕓 f) x  swap 𝕒 𝕓 (f $ swap 𝕒 𝕓 x)
    conj {𝕒} {𝕓} f x =
      begin
        (swap 𝕒 𝕓 f) x
      ≡⟨⟩
        (swap 𝕒 𝕓  f  swap 𝕒 𝕓) x
      ≡⟨⟩
        swap 𝕒 𝕓 (f $ swap 𝕒 𝕓 x)
      ≈˘⟨ cong-swap $ ≈-cong f swap-sym′ 
        swap 𝕒 𝕓 (f $ swap 𝕒 𝕓 $ swap 𝕒 𝕓 $ swap 𝕒 𝕓 x)
      ≡⟨⟩
        (swap 𝕒 𝕓  f  swap 𝕒 𝕓) (swap 𝕒 𝕓 $ swap 𝕒 𝕓 x)
      ≡⟨⟩
        (swap 𝕒 𝕓 f) (swap 𝕒 𝕓 $ swap 𝕒 𝕓 x)
      ≈˘⟨ distr-f 𝕒 𝕓 
        swap 𝕒 𝕓 (f $ swap 𝕒 𝕓 x)
       where distr-f = swap↔ f

private
  postulate
    𝕒 𝕓 : Atom
    𝕒≢𝕓 : 𝕒  𝕓

  unquoteDecl Swap-Maybe = DERIVE Swap [ quote Maybe , Swap-Maybe ]

  justAtom : Atom  Maybe Atom
  justAtom n =
    if n == 𝕒 then
      just 𝕒
    else
      nothing

  justAtom′ : Atom  Maybe Atom
  justAtom′ =  𝕒  𝕓  justAtom

  _ : justAtom 𝕒  just 𝕒
  _ rewrite ≟-refl 𝕒 = refl

  _ : justAtom 𝕓  nothing
  _ rewrite dec-no (𝕓  𝕒) (≢-sym 𝕒≢𝕓) .proj₂ = refl

  _ : justAtom′ 𝕒  nothing
  _ rewrite dec-no (𝕒  𝕓) 𝕒≢𝕓 .proj₂
          | ≟-refl 𝕒
          | dec-no (𝕓  𝕒) (≢-sym 𝕒≢𝕓) .proj₂
          = refl

  _ : justAtom′ 𝕓  just 𝕓
  _ rewrite ≟-refl 𝕓
          | dec-no (𝕓  𝕒) (≢-sym 𝕒≢𝕓) .proj₂
          | ≟-refl 𝕒
          | ≟-refl 𝕒
          = refl

module _
   _ : Enumerable∞ Atom 
  {A : Type }  _ : ISetoid A   _ : SetoidLaws A 
   _ : Swap A   _ : SwapLaws A 
  where

  --  * in the case of _→_, Equivariant′ is equivalent to Equivariant
  equivariant-equiv :  {f : A  A} 
    Equivariant f
    ══════════════
    Equivariant′ f
  equivariant-equiv {f = f} =  , 
      where
        open ≈-Reasoning

         : Equivariant f
            ─────────────────
            Equivariant′ f
         equiv-f = fin-f , refl
          where
            fin-f : FinSupp f
            fin-f = [] ,  x y _ _ a 
              begin
                 y  x  (f $  y  x  a)
              ≈˘⟨ cong-swap $ equiv-f _ _ 
                 y  x   y  x  f a
              ≈⟨ swap-sym′ 
                f a
              ) , λ _ _ ()

         : Equivariant′ f
            ─────────────────
            Equivariant f
         (fin-f , refl) a b {x} =
          begin
             a  b  f x
          ≈˘⟨ cong-swap $ fin-f .proj₂ .proj₁ _ _  ())  ()) _ 
             a  b   a  b  f ( a  b  x)
          ≈⟨ swap-sym′ 
            f ( a  b  x)
          

  private
    f′ : A  A
    f′ = id

    suppF′ = Atoms  []

    g′ : A  A
    g′ x = x

    f≗g : f′  g′
    f≗g _ = refl

    f≈g : f′  g′
    f≈g _ = ≈-refl

    ∃fin-f : ∃FinSupp f′
    ∃fin-f = suppF′ , λ _ _ _ _ _  swap-sym′

    fin-f : FinSupp f′
    fin-f = suppF′ ,  _ _ _ _ _  swap-sym′) ,  _ _ ())

    equiv-f : Equivariant f′
    equiv-f _ _ = ≈-refl

    equiv-f′ : Equivariant′ f′
    equiv-f′ = fin-f , refl

    instance
      Setoid-Bool : ISetoid Bool
      Setoid-Bool = λ where
        .relℓ  0ℓ
        ._≈_   _≡_

      SetoidLaws-Bool : SetoidLaws Bool
      SetoidLaws-Bool .isEquivalence = PropEq.isEquivalence

    postulate x y : Atom

    f : Atom  Bool
    f z = (z == x)  (z == y)
    suppF = List Atom  x  y  []
    -- fresh f = False

    finF : ∃FinSupp f
    finF = -, go
      where
        ∀x∉suppF :  {z}  z  suppF  f z  false
        ∀x∉suppF {z} z∉ with z  x
        ... | yes refl = ⊥-elim $ z∉ $ here refl
        ... | no _ with z  y
        ... | yes refl = ⊥-elim $ z∉ $ there $′ here refl
        ... | no _ = refl

        go :  𝕒 𝕓  𝕒  suppF  𝕓  suppF  f  swap 𝕓 𝕒  f
        go 𝕒 𝕓 𝕒∉ 𝕓∉ z with z  𝕓
        ... | yes refl rewrite ∀x∉suppF 𝕒∉ | ∀x∉suppF 𝕓∉ = refl
        ... | no _ with z  𝕒
        ... | yes refl rewrite ∀x∉suppF 𝕒∉ | ∀x∉suppF 𝕓∉ = refl
        ... | no _ = refl

    _ = finF .proj₁  suppF
       refl

    g : Atom  Bool
    g z = (z  x)  (z  y)
    suppG = List Atom  x  y  []
    -- fresh g = True
    -- NB: g is infinite, but has finite support!

    finG : ∃FinSupp g
    finG = -, go
      where
        ∀x∉suppG :  {z}  z  suppG  g z  true
        ∀x∉suppG {z} z∉ with z  x
        ... | yes refl = ⊥-elim $ z∉ $ here refl
        ... | no _ with z  y
        ... | yes refl = ⊥-elim $ z∉ $ there $′ here refl
        ... | no _ = refl

        go :  𝕒 𝕓  𝕒  suppG  𝕓  suppG  g  swap 𝕓 𝕒  g
        go 𝕒 𝕓 𝕒∉ 𝕓∉ z with z  𝕓
        ... | yes refl rewrite ∀x∉suppG 𝕒∉ | ∀x∉suppG 𝕓∉ = refl
        ... | no _ with z  𝕒
        ... | yes refl rewrite ∀x∉suppG 𝕒∉ | ∀x∉suppG 𝕓∉ = refl
        ... | no _ = refl

    -- T0D0: example where _≗_ is not the proper notion of equality
    -- module _ ⦃ _ : Toℕ Atom ⦄ where
    --   h : Atom → Bool
    --   h z = even? (toℕ z)
    --   -- ∄ supp h ⇔ ∄ fresh h

  -- Find the non-finSupp swappable example.
  -- ∙ ZFA ↝ ZFA+choice
  -- ∙ the set of all total orderings on atoms
  -- (empty support on the outside, infinite support inside each order)
  -- ∙ FOL: ultra-filter construction