nom-0.1.0.1: Name-binding & alpha-equivalence

Copyright(c) Murdoch J. Gabbay 2020
LicenseGPL-3
Maintainermurdoch.gabbay@gmail.com
Stabilityexperimental
PortabilityPOSIX
Safe HaskellNone
LanguageHaskell2010

Language.Nominal.Equivar

Contents

Description

This module contains functions for manipulating lists of representatives which represent orbit-finite equivariant maps, and applying them to arguments by matching representatives to arguments up to a swapping (permutation) action.

Synopsis

Equivariance

A structure is equivariant when it has a trivial swapping action:

swp _ _ a == a

Example: Every element of a Nameless type is equivariant.

Example: The mapping const True :: Atoms -> Bool is equivariant.

This second example is a particularly important example because it illustrates that "being equivariant" is not the same as "being nameless". To be equivariant, you only need to be symmetric.

A structure is equivariant orbit-finite when it can be represented by

  • taking finitely many representatives and
  • closing under the swapping action.

Example: the graph of the identity mapping id :: Atom -> Atom can be represented by closing [(a,a)] under all swappings. So id is equivariant orbit-finite.

Two equivariant orbit-finite maps may be compared for equality even though they may be infinite as functions, because they are finite as representatives-up-to-orbits. Opearationally for us here in Haskell, it suffices to inspect the representatives and compare them for unifiability using the functions from Unify.

We will build:

  • An equivariant function type KEvFun.
  • An equivariant orbit-finite space of maps KEvFinMap, and
  • an equivariant application operation $$, for applying a KEvFinMap (which under the hood is just a finite list of representatives) equivariantly to an argument.

Warning: When applying a map equivariantly using $$, It should hold that if (m $$ a) == b then supp b isSubsetOf supp a. If not, then unexpected behaviour may result. The general mathematical reasons for this are discussed with examples e.g. in closed nominal rewriting (see also author's pdfs). Thus in the terminology of that paper, we need m to be closed. One way to guarantee this is to ensure that b be Nameless, so that b is a type like Bool or Int and supp b is guaranteed empty. In practice for the cases we care about, b will indeed always be Nameless.

newtype KEvFun s a b Source #

KEvFun s a b is just a -> b in a wrapper. But, we give this wrapped function trivial swapping and support. (For a usage example see the source code for Val.)

Functions need not be finite and are not equality types in general, so we cannot computably test that the actual function wrapped by the programmer actually does have a trivial swapping action (i.e. really is equivariant).

It's the programmer's job to only insert truly equivariant functions here. Non-equivariant elements may lead to incorrect runtime behaviour.

On an equivariant orbit-finite type, the compiler can step in with more stringent guarantees. See e.g. KEvFinMap.

Constructors

EvFun 

Fields

  • runEvFun :: a -> b

    Function in a wrapper

Instances
(Typeable s, Swappable a, Swappable b) => KSupport s (KEvFun s a b) Source # 
Instance details

Defined in Language.Nominal.Equivar

Methods

ksupp :: proxy s -> KEvFun s a b -> Set (KAtom s) Source #

(Typeable s, Swappable a, Swappable b) => Swappable (KEvFun s a b) Source # 
Instance details

Defined in Language.Nominal.Equivar

Methods

kswp :: Typeable s0 => KAtom s0 -> KAtom s0 -> KEvFun s a b -> KEvFun s a b Source #

IEq (KEvFun k a b) Source # 
Instance details

Defined in Language.Nominal.Examples.IdealisedEUTxO

Methods

iEq :: KEvFun k a b -> KEvFun k a b -> IO Bool

type EvFun = KEvFun Tom Source #

Tom-equivariant function-space

Equivariant lists and maps

Now for some basic functions that filter an input list down to a nub of representatives.

kevRep :: KUnifyPerm s a => proxy s -> [a] -> [a] Source #

Filter a list down to one representative from each atoms-orbit (choosing first representative of each orbit).

evRep :: UnifyPerm a => [a] -> [a] Source #

Instance for Tom atom type.

kevNub :: (Ord a, KUnifyPerm s a) => proxy s -> Map a b -> Map a b Source #

Restrict a Map to its equivariant nub, discarding all but one of each key-orbit

evNub :: (Ord a, UnifyPerm a) => Map a b -> Map a b Source #

Instance for unit atom type.

Lookup

So we have a finite set of representative pairs. How do we unify a putative input with a representative to find a matching output?

kevLookup :: (KUnifyPerm s a, KUnifyPerm s b) => proxy s -> Map a b -> a -> Maybe b Source #

Equivariantly apply a map m to an element a.

  • If a == ren r a' for some r :: Ren and m a' == b',
  • then kevLookup m a == ren r b'.

For this to work, we require types a and b to support a ren action, meaning they should support notions of unification up to permutation.

evLookup :: (UnifyPerm a, UnifyPerm b) => Map a b -> a -> Maybe b Source #

kevLookup instantiated to a Tom.

kevLookupList' :: (KUnifyPerm s a, KUnifyPerm s b) => proxy s -> [(a, b)] -> a -> Maybe (a, b) Source #

Equivariantly apply a list of pairs, which we assume represents a map, to an element. Also returns the source element.

kevLookupList :: (KUnifyPerm s a, KUnifyPerm s b) => proxy s -> [(a, b)] -> a -> Maybe b Source #

Equivariantly apply a list of pairs (which we assume represents a map), to an element

evLookupList :: (UnifyPerm a, UnifyPerm b) => [(a, b)] -> a -> Maybe b Source #

Equivariantly apply a list of pairs (which we assume represents a map), to an element. Tom version.

Orbit-finite equivariant maps

KEvFinMap is a type for orbit-finite equivariant maps (contrast with KEvFun, a type for equivariant functions which need not be orbit-finite). Values of KEvFinMap must be constructed using

Under the hood, KEvFinMap s a b has just one constructor:

DefAndRep b [(a, b)]  

DefAndRep stands for Default and list of Representatives. We represent an orbit-finite equivariant map from a to b as

  • A default value in b, and
  • a list of key-value pairs, to be applied up to permuting s-sorted atoms in the keys.

DefAndRep does not store the atoms it wants permuted, in its structure. It's just a pair of an element in b and a list of pairs from [(a, b)]. At the point where we use $$ to equivariantly apply some DefAndRep structure to some argument in a, we specify over which atoms we wish to be equivariant.

Thus: calling this KEvFinMap is convenient but slighly misleading: the equivariance lies in the $$-application of a KEvFinMap-wrapped collection of representatives, to an argument.

data KEvFinMap s a b Source #

A type for orbit-finite equivariant maps.

Instances
(Typeable s, Swappable a, Swappable b) => KSupport s (KEvFinMap s a b) Source #

We insist a and b be k-swappable so that the mathematical notion of support (which is based on nominal sets) makes sense.

Operationally, we don't care: if see something of type KEvFinMap s a b, we return Set.empty.

Instance details

Defined in Language.Nominal.Equivar

Methods

ksupp :: proxy s -> KEvFinMap s a b -> Set (KAtom s) Source #

(KUnifyPerm s a, KUnifyPerm s b, Eq b) => KUnifyPerm s (KEvFinMap s a b) Source # 
Instance details

Defined in Language.Nominal.Equivar

Methods

kunifyPerm :: proxy s -> KEvFinMap s a b -> KEvFinMap s a b -> KRen s Source #

ren :: KRen s -> KEvFinMap s a b -> KEvFinMap s a b Source #

(Arbitrary a, UnifyPerm a, Eq a, Arbitrary b, Eq b) => Arbitrary (EvFinMap a b) Source # 
Instance details

Defined in Language.Nominal.Properties.SpecUtilities

Methods

arbitrary :: Gen (EvFinMap a b) #

shrink :: EvFinMap a b -> [EvFinMap a b] #

(KUnifyPerm s a, Eq b) => Eq (KEvFinMap s a b) Source #

KEvFinMap is compared for equality by comparing the default value and the representatives, up to permutations.

Edge case: If a codomain type is orbit-finite (e.g. Bool and '(Atom,Atom)', with two orbits, or KAtom with one), and representatives exhaust all possibilities, then the default value will never be queried, yet it will still be considered in our equality test.

Instance details

Defined in Language.Nominal.Equivar

Methods

(==) :: KEvFinMap s a b -> KEvFinMap s a b -> Bool #

(/=) :: KEvFinMap s a b -> KEvFinMap s a b -> Bool #

(Show b, Show a) => Show (KEvFinMap s a b) Source # 
Instance details

Defined in Language.Nominal.Equivar

Methods

showsPrec :: Int -> KEvFinMap s a b -> ShowS #

show :: KEvFinMap s a b -> String #

showList :: [KEvFinMap s a b] -> ShowS #

Generic (KEvFinMap s a b) Source # 
Instance details

Defined in Language.Nominal.Equivar

Associated Types

type Rep (KEvFinMap s a b) :: Type -> Type #

Methods

from :: KEvFinMap s a b -> Rep (KEvFinMap s a b) x #

to :: Rep (KEvFinMap s a b) x -> KEvFinMap s a b #

Swappable a => Swappable (KEvFinMap s a b) Source # 
Instance details

Defined in Language.Nominal.Equivar

Methods

kswp :: Typeable s0 => KAtom s0 -> KAtom s0 -> KEvFinMap s a b -> KEvFinMap s a b Source #

type Rep (KEvFinMap s a b) Source # 
Instance details

Defined in Language.Nominal.Equivar

type Rep (KEvFinMap s a b) = D1 (MetaData "KEvFinMap" "Language.Nominal.Equivar" "nom-0.1.0.1-3UtpNOnqRlSC6EhVIia7SR" False) (C1 (MetaCons "DefAndRep" PrefixI False) (S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 (Nameless b)) :*: S1 (MetaSel (Nothing :: Maybe Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 [(a, Nameless b)])))

type EvFinMap a b = KEvFinMap Tom a b Source #

KEvFinMap at a Tom. Thus, a type for orbit-finite Tom-equivariant maps.

($$) :: forall s a b. (KUnifyPerm s a, Eq b) => KEvFinMap s a b -> a -> b infixr 0 Source #

Equivariant application of an orbit-finite map. Given a map (expressed as finitely many representative pairs) and an argument ...

  • it tries to rename atoms to match the argument to the first element of one of its pairs and
  • if it finds a match, it maps to the second element of that pair, suitably renamed.

constEvFinMap :: KUnifyPerm s a => b -> KEvFinMap s a b Source #

A constant equivariant map. We assume the codomain is Nameless.

>>> (constEvFinMap 42 :: EvFinMap Char Int) $$ 'x'
42
>>> (constEvFinMap 0 :: EvFinMap Atom Int) $$ a
0 

extEvFinMap :: forall s a b. (KUnifyPerm s a, Eq a, Eq b) => a -> b -> KEvFinMap s a b -> KEvFinMap s a b Source #

Extends a map with a new orbit, by specifying a representative a maps to b. We assume codomain b is Nameless, as discussed above.

>>> (extEvFinMap 'x' 7 $ (constEvFinMap 42 :: EvFinMap Char Int)) $$ 'x'
7
>>> (extEvFinMap 'x' 7 $ (constEvFinMap 42 :: EvFinMap Char Int)) $$ 'y'
42
>>> let [m, n, o] = exit $ freshNames [(), (), ()]
>>> m == n
False
>>> unifiablePerm m n
True
>>> (extEvFinMap m 7 $ (constEvFinMap 42 :: EvFinMap (Name ()) Int)) $$ n
7
>>> (extEvFinMap o 8 (extEvFinMap m 7 $ (constEvFinMap 42 :: EvFinMap (Name ()) Int))) $$ n
8

evFinMap Source #

Arguments

:: (KUnifyPerm s a, Eq a, Eq b) 
=> b

Default value.

-> [(a, b)]

List of exceptional argument-value pairs. In case of conflict, later pairs overwrite earlier pairs.

-> KEvFinMap s a b 

Constructs an orbit-finite equivariant map by prescribing a default value, and finitely many argument-value pairs. We assume the codomain is Nameless, as discussed above.

>>> let f = evFinMap 42 [('x', 7), ('y', 5), ('x', 13)] :: EvFinMap Char Int
>>> map (f $$) ['x', 'y', 'z']
[13,5,42]
>>> let atmEq = evFinMap False [((a,a), True)] :: EvFinMap (Atom, Atom) Bool
>>> map (atmEq $$) [(b,b), (c,c), (a,c), (b,c)]
[True,True,False,False]
>>> let g = evFinMap 2 [((a,a), 0), ((b,c), 1)] :: EvFinMap (Atom, Atom) Int
>>> map (g $$) [(b,b), (c,c), (a,c), (b,c)]
[0,0,1,1]

fromEvFinMap :: KEvFinMap s a b -> (b, [(a, b)]) Source #

Extracts default value und list of exceptional argument-value pairs from an EvFinMap.

>>> fromEvFinMap $ (evFinMap 42 [('x', 7), ('y', 5), ('x', 13)] :: EvFinMap Char Int)
(42,[('y',5),('x',13)])

Tests

Property-based tests are in Language.Nominal.Properties.EquivarSpec.