En esta clase, exploraremos las estructuras algebraicas básicas y el diseño de librerías. El proyecto grupal se llevará a cabo en esa dirección. Si prefieren trabajar en otro tema, aquí va un proyecto sobre automorfismos interiores del grupo
Magma
.IsAssociative : Magma → Prop
.Por comodidad, llamaremos una función currificada de signatura X → X → X
una operación en X.
def Op (X: Type) : Type := X → X → X
⟨X, μ⟩
donde X
es un tipo (llamado el carrier del magma) y μ : Op X
es una operación en X
.l
será el (X : Type l) × Op X
. En particular, es un tipo de nivel l + 1
(ya que se cuantifica sobre Type l
).Al declarar el
structure Magma.{l} : Type (l + 1) where
carrier : Type l
op : Op carrier
Magma.carrier
proyecta un magma ⟨X, μ⟩
a su tipo sub-yacente X
(primera proyección). Si M : Magma
, se puede utilizar la notación M.carrier
.Magma.op
proyecta un magma ⟨X, μ⟩
a la operación μ : Op X
(segunda proyección). Si M : Magma
, se puede utilizar la notación M.op
.M = ⟨M.carrier, M.op⟩
(conocida como Para construir el magma
def NatAdd : Magma where
carrier := Nat
op := Nat.add
#check NatAdd -- NatAdd : Magma
#check NatAdd.carrier -- NatAdd.carrier : Type
#check NatAdd.op -- NatAdd.op : Op NatAdd.carrier
where
realmente ayuda (en comparación con usar :=
).def NatAdd : Magma := Magma.mk Nat Nat.add
, o simplemente def NatAdd : Magma := ⟨Nat, Nat.add⟩
.(· + ·)
como notación para Nat.add
.(· * ·)
en un tipo X
es asociativa si, para todos x y z : X
, tenemos una igualdad (x * y) * z = x * (y * z)
en X
.class Magma.IsAssociative.{l} (magma : Magma.{l}) : Prop where
assoc : ∀ x y z : magma.carrier,
magma.op (magma.op x y) z = magma.op x (magma.op y z)
(NatAdd := ⟨Nat, (· + ·)⟩ : Magma)
, tiene una operación asociativa.M : Magma
, se puede utlizar la notación M.IsAssociative
en lugar de Magma.IsAssociative M
.instance instAssociativeNatAdd : NatAdd.IsAssociative where
assoc := Nat.add_assoc
⟨Nat, (· * ·)⟩
y que registren una instancia de asociatividad en ello.NatAdd
es conmutativo?Como dijimos, un semigrupo es por definición un magma asociativo. Formalmente, eso significa que el tipo de los semigrupos es el
Esto se puede formalizar extendiendo la estructura Magma
.
structure Semigroup.{l} extends Magma.{l} : Type (l + 1) where
assoc : toMagma.IsAssociative
Esto significa que si S : Semigroup
, entonces S.toMagma
es el magma subyacente y la operación S.toMagma.op
es associativa en S.toMagma.carrier
. Esta última información está contenida en S.assoc : S.toMagma.IsAssociative
.
Con la construcción del tipo de los semigrupos como ⟨X, μ, p⟩ : Semigroup
, donde:
X
es un tipo,μ : Op X
es una operación en X
,p
es una prueba de la proposición Magma.IsAssociative ⟨X, μ⟩
.El hecho de que la propiedad de asociatividad sea una proposición garantiza que el tipo de los semigrupos sea un sub-tipo del tipo de los magmas. Además, eso permite identificar ⟨X, μ, p⟩
con ⟨X, μ, p'⟩
como semigrupos (¡no importa cómo se demuestre que μ : Op X
es asociativa!).
e : X
es un elemento neutro para una operación μ : Op X
.class Magma.HasNeutral.{l} (magma : Magma.{l}) (e : magma.carrier) : Prop where
left_neutral : ∀ x : magma.carrier, magma.op e x = x
right_neutral : ∀ x : magma.carrier, magma.op x e = x
(M : Magma)
y (e : M.carrier)
, podemos escribir M.HasNeutral e
en lugar de Magma.HasNeutral M e
(es la dot notation, que hemos visto antes).En el proyecto, se les pedirá que demuestren que
instance instHasNeutralNatAddZero : NatAdd.HasNeutral (0 : Nat) where
left_neutral := by
sorry
right_neutral := by
sorry
apply Magma.HasNeutral.mk
(o constructor
) y dsimp [NatAdd]
para simplificar el gol y convertirlo en una propiedad de los enteros naturales.∀ (n : Nat), 0 + n = n
y ∀ (n : Nat), n + 0 = n
. Una de estas dos propiedades requiere una prueba por inducción.Magma.HasNeutral
, podemos definir el tipo de los monoides como el ⟨X, μ, p, e, q⟩
donde q
es una prueba de la propiedad que e
es un elemento neutro en el el magma ⟨X, μ⟩
.X
, μ
y e
son datos, mientras que p
y q
son propiedades. En general, se recomienda, como parte del diseño de una librería, distinguir bien entre las dos cosas (así como en otros lenguajes de programación se distingue entre command y query).Monoid
es que, en el término ⟨X, μ, p, e, q⟩
, tenemos un elemento marcado e : X
, al cual nos podemos referir si queremos por ejemplo definir elementos invertibles en ⟨X, μ⟩
.e : X
).Pudimos también plantear la definición de tener un elemento neutro de la siguiente forma, utilizando un cuantificador existencial:
class Magma.HasNeutral.{l} (magma : Magma.{l}) : Prop where
neutral : ∃ (e : magma.carrier), ∀ x : magma.carrier,
(magma.op e x = x) ∧ (magma.op x e = x)
∃
, el término neutral
ahora tiene como tipo una proposición. Entonces, si usáramos esa versión del predicado Magma.HasNeutral
, el tipo Monoid
sería por definición un sub-tipo de Semigroup
.Les propongo que trabajen en uno de los siguientes dos archivos: