Copyright | (c) Murdoch J. Gabbay 2020 |
---|---|
License | GPL-3 |
Maintainer | murdoch.gabbay@gmail.com |
Stability | experimental |
Portability | POSIX |
Safe Haskell | None |
Language | Haskell2010 |
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
- newtype KEvFun s a b = EvFun {
- runEvFun :: a -> b
- type EvFun = KEvFun Tom
- kevRep :: KUnifyPerm s a => proxy s -> [a] -> [a]
- evRep :: UnifyPerm a => [a] -> [a]
- kevNub :: (Ord a, KUnifyPerm s a) => proxy s -> Map a b -> Map a b
- evNub :: (Ord a, UnifyPerm a) => Map a b -> Map a b
- kevLookup :: (KUnifyPerm s a, KUnifyPerm s b) => proxy s -> Map a b -> a -> Maybe b
- evLookup :: (UnifyPerm a, UnifyPerm b) => Map a b -> a -> Maybe b
- kevLookupList' :: (KUnifyPerm s a, KUnifyPerm s b) => proxy s -> [(a, b)] -> a -> Maybe (a, b)
- kevLookupList :: (KUnifyPerm s a, KUnifyPerm s b) => proxy s -> [(a, b)] -> a -> Maybe b
- evLookupList :: (UnifyPerm a, UnifyPerm b) => [(a, b)] -> a -> Maybe b
- data KEvFinMap s a b
- type EvFinMap a b = KEvFinMap Tom a b
- ($$) :: forall s a b. (KUnifyPerm s a, Eq b) => KEvFinMap s a b -> a -> b
- constEvFinMap :: KUnifyPerm s a => b -> KEvFinMap s a b
- extEvFinMap :: forall s a b. (KUnifyPerm s a, Eq a, Eq b) => a -> b -> KEvFinMap s a b -> KEvFinMap s a b
- evFinMap :: (KUnifyPerm s a, Eq a, Eq b) => b -> [(a, b)] -> KEvFinMap s a b
- fromEvFinMap :: KEvFinMap s a b -> (b, [(a, b)])
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 aKEvFinMap
(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
. 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 isSubsetOf
supp am
to be closed. One way to guarantee this is to ensure that b
be
, so that Nameless
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
is just KEvFun
s a ba -> 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
.
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).
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
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 somer :: Ren
andm a' == b'
, - then
kevLookup m a == ren r b'
.
For this to work, we require types a
and b
to support a
action, meaning they should support notions of unification up to permutation.ren
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.
version.Tom
Orbit-finite equivariant maps
is a type for orbit-finite equivariant maps (contrast with KEvFinMap
, a type for equivariant functions which need not be orbit-finite).
Values of KEvFun
must be constructed using KEvFinMap
,constEvFinMap
andextEvFinMap
.evFinMap
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.
A type for orbit-finite equivariant maps.
Instances
(Typeable s, Swappable a, Swappable b) => KSupport s (KEvFinMap s a b) Source # | We insist Operationally, we don't care: if see something of type |
(KUnifyPerm s a, KUnifyPerm s b, Eq b) => KUnifyPerm s (KEvFinMap s a b) Source # | |
(Arbitrary a, UnifyPerm a, Eq a, Arbitrary b, Eq b) => Arbitrary (EvFinMap a b) Source # | |
(KUnifyPerm s a, Eq b) => Eq (KEvFinMap s a b) Source # |
Edge case: If a codomain type is orbit-finite (e.g. |
(Show b, Show a) => Show (KEvFinMap s a b) Source # | |
Generic (KEvFinMap s a b) Source # | |
Swappable a => Swappable (KEvFinMap s a b) Source # | |
type Rep (KEvFinMap s a b) Source # | |
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)]))) |
($$) :: 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
, as discussed above. Nameless
>>>
(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
:: (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
, as discussed above. Nameless
>>>
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.