A
and variable a
this type: val A = "A" :: Type val a = "a" :: A
val _ = " " :: _
.fansi
method, its type can be .typ
using the .typ
: println(a) > a : (A : U_0) println(a.fansi) > a println(a.typ) > A : U_0 println(a.typ.fansi) > A
val a : Term = "a" :: A
A
is the type in the ProvingGround library, i.e. HoTT is a type, and Term
is a type in Scala.::
, and if we already have a term, then we can check its type using !:
a !: A
1 =:= 2
(which has no values), types 1 =:= 1
, 2 =:= 2
(in which by one value), etc.Ba
, which depends on the values of type A
, and a variable of this type: val Ba = "B(_ : A)" :: A ->: Type val b = "b" :: Ba(a)
:->
:~>
, ->:
~>:
With colons on the left - for lambdas (ie, the functions themselves), with colons on the right - for types of functions. With hyphens - for ordinary functions, with tildes - for dependent functions (that is, for which not only the value, but also the type of the value depends on the argument). As an example, the identity function val id = A :~> (a :-> a) !: A ~>: (A ->: A)
val f : FuncLike[Term, Term] = a :~> b !: a ~>: Ba(a) val f : Term => Term = a :~> b !: a ~>: Ba(a)
val Bool = "Boolean" :: Type val BoolInd = ("true" ::: Bool) |: ("false" ::: Bool) =: Bool val tru :: fls :: HNil = BoolInd.intros
val _ = (...) |: (...) |: (...) =: _
|:
n
": val Nat = "Nat" :: Type val NatInd = ("0" ::: Nat) |: ("succ" ::: Nat -->>: Nat) =: Nat val zero :: succ :: HNil = NatInd.intros val one = succ(zero) !: Nat val two = succ(one) !: Nat // ...
n
” and “minus positive integer n
minus 1” uses the already defined type of positive integers: val Int = "Integer" :: Type val IntInd = ("pos" ::: Nat ->>: Int) |: ("neg" ::: Nat ->>: Int) =: Int val pos :: neg :: HNil = IntInd.intros
A
value list type has “empty list” and “list with type A
head and ListA
type ListA
” ListA
: val ListA = "List(A)" :: Type val ListAInd = ("nil" ::: ListA) |: ("cons" ::: A ->>: ListA -->>: ListA) =: ListA val nil :: cons :: HNil = ListAInd.intros
val BTree = "BTree" :: Type val BTreeInd = ("leaf" ::: BTree) |: ("fork" ::: BTree -->>: BTree -->>: BTree) =: BTree val leaf :: fork :: HNil = BTreeInd.intros
val BTree = "BTree" :: Type val BTreeInd = ("leaf" ::: BTree) |: ("fork" ::: (Bool -|>: BTree) -->>: BTree) =: BTree val leaf :: fork :: HNil = BTreeInd.intros
Vec
vector or equality type Id
, then you should use =::
instead of =:
arrow with a tilde and refer to the type inside constructors (_ -> _(n))
in the returned type, (_ :> _(n))
in the received type, not just _(n)
. For example, the type of vector of length n
: val VecA = "Vec(A)" :: Nat ->: Type val n = "n" :: Nat val VecAInd = ("nil" ::: (VecA -> VecA(zero) )) |: {"cons" ::: n ~>>: (A ->>: (VecA :> VecA(n) ) -->>: (VecA -> VecA(succ(n)) ))} =:: VecA val vnil :: vcons :: HNil = VecAInd.intros
val Fin = "Fin" :: Nat ->: Type val FinInd = {"FZ" ::: n ~>>: (Fin -> Fin(succ(n)) )} |: {"FS" ::: n ~>>: ((Fin :> Fin(n) ) -->>: (Fin -> Fin(succ(n)) ))} =:: Fin val fz :: fs :: HNil = FinInd.intros
Fin(zero)
. There is exactly one Fin(one)
value: val fz0 = fz(zero) !: Fin(one)
Fin(two)
values: val fz1 = fz(one) !: Fin(two) val fs1 = fs(one)(fz0) !: Fin(two)
Fin(three)
values: fz(two) !: Fin(three) fs(two)(fz1) !: Fin(three) fs(two)(fs1) !: Fin(three)
List(A)
is a family, and Vec(A)(n)
is a family with respect to type A
, but an indexed type with respect to the natural index n
. An inductive type is a type whose constructors for “next” values can use “previous” values (as with types Nat
, List
, etc.). Vec(A)(n)
with fixed A
is inductive type, but is not with fixed n
. There are currently no inductive type families in ProvingGround, i.e. it is impossible, having an inductive definition, for example, of the type List(A)
, it is easy to obtain an inductive definition of the types List(B)
, List(Nat)
, List(Bool)
, List(List(A))
, etc. But you can emulate families using indexed types: val List = "List" :: Type ->: Type val ListInd = {"nil" ::: A ~>>: (List -> List(A) )} |: {"cons" ::: A ~>>: (A ->>: (List :> List(A) ) -->>: (List -> List(A) ))} =:: List val nil :: cons :: HNil = ListInd.intros cons(Nat)(zero)(cons(Nat)(one)(cons(Nat)(two)(nil(Nat)))) !: List(Nat) // 0, 1, 2
val Vec = "Vec" :: Type ->: Nat ->: Type val VecInd = {"nil" ::: A ~>>: (Vec -> Vec(A)(zero) )} |: {"cons" ::: A ~>>: n ~>>: (A ->>: (Vec :> Vec(A)(n) ) -->>: (Vec -> Vec(A)(succ(n)) ))} =:: Vec val vnil :: vcons :: HNil = VecInd.intros vcons(Bool)(two)(tru)(vcons(Bool)(one)(fls)(vcons(Bool)(zero)(tru)(vnil(Bool)))) !: Vec(Bool)(succ(two)) // 3- tru, fls, tru
HList
): val HLst = "HList" :: Type ->: Type val HLstInd = {"nil" ::: A ~>>: (HLst -> HLst(A) )} |: {"cons" ::: A ~>>: (A ->>: (B ~>>: ((HLst :> HLst(B) ) -->>: (HLst -> HLst(A) ))))} =:: HLst val hnil :: hcons :: HNil = HLstInd.intros
HList
in the ProvingGround library, which is written on top of Shapeless, in which the main building HList
is HList
. {-# Language GADTs #-} data Expr a where ELit :: a -> Expr a ESucc :: Expr Int -> Expr Int EIsZero :: Expr Int -> Expr Bool EIf :: Expr Bool -> Expr a -> Expr a -> Expr a
sealed trait Expr[A] case class ELit[A](lit: A) extends Expr[A] case class ESucc(num: Expr[Int]) extends Expr[Int] case class EIsZero(num: Expr[Int]) extends Expr[Boolean] case class EIf[A](cond: Expr[Boolean], thenExpr: Expr[A], elseExpr: Expr[A]) extends Expr[A]
val Expr = "Expr" :: Type ->: Type val ExprInd = {"ELit" ::: A ~>>: (A ->>: (Expr -> Expr(A) ))} |: {"ESucc" ::: Expr(Nat) ->>: (Expr -> Expr(Nat) )} |: {"EIsZero" ::: Expr(Nat) ->>: (Expr -> Expr(Bool) )} |: {"EIf" ::: A ~>>: (Expr(Bool) ->>: Expr(A) ->>: Expr(A) ->>: (Expr -> Expr(A) ))} =:: Expr val eLit :: eSucc :: eIsZero :: eIf :: HNil = ExprInd.intros
val A = "A" :: Type val B = "B" :: Type val C = "C" :: Type val Functor = "Functor" :: (Type ->: Type) ->: Type val F = "F(_ : U_0)" :: Type ->: Type val Fmap = A ~>: (B ~>: ((A ->: B) ->: (F(A) ->: F(B) ))) val FunctorInd = {"functor" ::: F ~>>: (Fmap ->>: (Functor -> Functor(F) ))} =:: Functor val functor :: HNil = FunctorInd.intros
val as = "as" :: List(A) val indList_map = ListInd.induc(A :~> (as :-> (B ~>: ((A ->: B) ->: List(B) )))) // , val mapas = "map(as)" :: B ~>: ((A ->: B) ->: List(B)) val f = "f" :: A ->: B val map = indList_map(A :~> (B :~> (f :-> nil(B) )))(A :~> (a :-> (as :-> (mapas :-> (B :~> (f :-> cons(B)(f(a))(mapas(B)(f)) )))))) !: A ~>: (List(A) ->: (B ~>: ((A ->: B) ->: List(B) ))) val listFunctor = functor(List)(A :~> (B :~> (f :-> (as :-> map(A)(as)(B)(f) )))) !: Functor(List)
val fmap = "fmap" :: A ~>: (B ~>: ((A ->: B) ->: (F(A) ->: F(B) ))) val Fmap_id = A ~>: ( fmap(A)(A)(id(A)) =:= id(F(A)) ) val f = "f" :: A ->: B val g = "g" :: B ->: C val compose = A :~> (B :~> (C :~> (f :-> (g :-> (a :-> g(f(a)) ))))) !: A ~>: (B ~>: (C ~>: ((A ->: B) ->: ((B ->: C) ->: (A ->: C))))) val Fmap_compose = A ~>: (B ~>: (C ~>: (f ~>: (g ~>: ( fmap(A)(C)(compose(A)(B)(C)(f)(g)) =:= compose(F(A))(F(B))(F(C))(fmap(A)(B)(f))(fmap(B)(C)(g)) ))))) val FunctorInd = {"functor" ::: F ~>>: (fmap ~>>: (Fmap_id ->>: (Fmap_compose ->>: (Functor -> Functor(F) ))))} =:: Functor val functor :: HNil = FunctorInd.intros
mkPair(a, b) !: Sgma(a !: A, Ba(a)) one.refl !: (one =:= one) one.refl !: IdentityTyp(Nat, one, one) two.refl !: (two =:= two) (one =:= two) !: Type
val Id = "Id" :: A ~>: (A ->: A ->: Type) val IdInd = ("refl" ::: A ~>>: a ~>>: (Id -> Id(A)(a)(a) )) =:: Id val refl :: HNil = IdInd.intros refl(Nat)(two) !: Id(Nat)(two)(two)
A ->: B
is a function from A
to B
, on the other hand, it is a logical formula “from Statement A
follows B
”. And, on the one hand, (A ->: B) ->: A ->: B
is a type of higher order function that takes as input the function A ->: B
and a value of type A
and returns this function applied to this value, i.e. value of type B
On the other hand, this is an inference rule in modus ponens logic - “if it is true that A
follows from A
, and A
is true, then B
is true”. From this point of view, types are statements, and type values are proofs of these statements. And the statement is true if the corresponding type is populated, i.e. There is a value of this type. The logical “and” corresponds to the product of types, the logical “or” to the sum of the types, the logical “not” to the type A ->: Zero
, i.e. functions in an empty type. So in the theory of types logic arises. True, not any logic, but the so-called intuitionistic or constructive, i.e. logic without the law of elimination of the third. Indeed, generally speaking, one cannot construct a value of type PlusTyp(A, A ->: Zero)
(if one could not prove A
, then this does not mean that one could prove non- A
). Interestingly, the negation to the negation of the law of excluding the third is true: val g = "g" :: PlusTyp(A, A ->: Zero) ->: Zero val g1 = a :-> g(PlusTyp(A, A ->: Zero).incl1(a)) !: A ->: Zero g :-> g(PlusTyp(A, A ->: Zero).incl2(g1)) !: (PlusTyp(A, A ->: Zero) ->: Zero) ->: Zero
a1 =:= a2
is a statement, which means type. Type dependent, since depends on the values of a1, a2
some type A
If a1, a2
different, then there should not be a way to construct a value of this type, since the statement is false. If they are the same, then there should be a way to construct a value, since the statement is true, so our inductive type has a single constructor refl(A)(a) !: Id(A)(a)(a)
(or a.refl !: (a =:= a)
for the built-in equality type). val LTE = "≤" :: Nat ->: Nat ->: Type val LTEInd = {"0 ≤ _" ::: m ~>>: (LTE -> LTE(zero)(m) )} |: {"S _ ≤ S _" ::: n ~>>: m ~>>: ((LTE :> LTE(n)(m) ) -->>: (LTE -> LTE(succ(n))(succ(m)) ))} =:: LTE val lteZero :: lteSucc :: HNil = LTEInd.intros
val Circle = "S^1" :: Type val base = "base" :: Circle // val loop = "loop" :: (base =:= base) //
val Sphere = "S^2" :: Type val base = "base" :: Sphere // val surf = "surf" :: (base.refl =:= base.refl) // // val surf = "surf" :: IdentityTyp(base =:= base, base.refl, base.refl) // val surf = "surf" :: IdentityTyp(IdentityTyp(Sphere, base, base), base.refl, base.refl)
.rec
, .induc
, i.e. recursion (aka recursor) and induction are eliminators into a permanent and dependent type, respectively, with which you can carry out pattern matching (pattern matching), if necessary - recursive. For example, you can define a logical "not": val b = "b" :: Bool val recBB = BoolInd.rec(Bool) val not = recBB(fls)(tru)
match { case true => false case false => true }
not(tru) == fls not(fls) == tru
val recBBB = BoolInd.rec(Bool ->: Bool) val and = recBBB(b :-> b)(b :-> fls)
// match { case true => (b => b) case false => (b => false) }
and(fls)(tru) == fls and(tru)(tru) == tru
val n = "n" :: Nat val m = "m" :: Nat val recNN = NatInd.rec(Nat) val double = recNN(zero)(n :-> (m :-> succ(succ(m)) ))
// match { case Zero => Zero case Succ(n) => val m = double(n) m + 2 }
println(double(two).fansi) > succ(succ(succ(succ(0))))
val recNNN = NatInd.rec(Nat ->: Nat) val addn = "add(n)" :: Nat ->: Nat val add = recNNN(m :-> m)(n :-> (addn :-> (m :-> succ(addn(m)) )))
// match { case Zero => (m => m) case Succ(n) => val addn = add(n) m => addn(m) + 1 }
println(add(two)(three).fansi) > succ(succ(succ(succ(succ(0)))))
val vn = "v_n" :: VecA(n) val vm = "v_m" :: VecA(m) val indVVV = VecAInd.induc(n :~> (vn :-> (m ~>: (VecA(m) ->: VecA(add(n)(m)) )))) val concatVn = "concat(v_n)" :: (m ~>: (VecA(m) ->: VecA(add(n)(m)) )) val vconcat = indVVV(m :~> (vm :-> vm))(n :~> (a :-> (vn :-> (concatVn :-> (m :~> (vm :-> vcons(add(n)(m))(a)(concatVn(m)(vm)) ))))))
m ~>: (VecA(m) ->: VecA(add(n)(m)))
- indeed, this type depends on n
from the vector (the first concatenation argument), which we deconstruct when comparing with the pattern: // match { case (Zero, Nil) => (vm => vm) case (Succ(n), Cons(a)(vn)) => val concatVn = concat(vn) vm => Cons(a)(concatVn(vm)) }
val a = "a" :: A val a1 = "a1" :: A val a2 = "a2" :: A val a3 = "a3" :: A val a4 = "a4" :: A val vect = vcons(one)(a)(vcons(zero)(a1)(vnil)) val vect1 = vcons(two)(a2)(vcons(one)(a3)(vcons(zero)(a4)(vnil))) println(vconcat(two)(vect)(three)(vect1).fansi) > cons(succ(succ(succ(succ(0)))))(a)(cons(succ(succ(succ(0))))(a1)(cons(succ(succ(0)))(a2)(cons(succ(0))(a3)(cons(0)(a4)(nil)))))
add(n)(n) =:= double(n)
. val indN_naddSm_eq_S_naddm = NatInd.induc(n :-> (m ~>: ( add(n)(succ(m)) =:= succ(add(n)(m)) ))) val hyp1 = "n+Sm=S(n+m)" :: (m ~>: ( add(n)(succ(m)) =:= succ(add(n)(m)) )) val lemma = indN_naddSm_eq_S_naddm(m :~> succ(m).refl)(n :~> (hyp1 :-> (m :~> IdentityTyp.extnslty(succ)( add(n)(succ(m)) )( succ(add(n)(m)) )( hyp1(m) ) ))) !: n ~>: m ~>: ( add(n)(succ(m)) =:= succ(add(n)(m)) ) val lemma1 = IdentityTyp.extnslty(succ)( add(n)(succ(n)) )( succ(add(n)(n)) )( lemma(n)(n) ) val indN_naddn_eq_2n = NatInd.induc(n :-> ( add(n)(n) =:= double(n) )) val hyp = "n+n=2*n" :: ( add(n)(n) =:= double(n) ) val lemma2 = IdentityTyp.extnslty( m :-> succ(succ(m)) )( add(n)(n) )( double(n) )(hyp) indN_naddn_eq_2n(zero.refl)(n :~> (hyp :-> IdentityTyp.trans(Nat)( add(succ(n))(succ(n)) )( succ(succ(add(n)(n))) )( double(succ(n)) )(lemma1)(lemma2) )) !: n ~>: ( add(n)(n) =:= double(n) )
(...) |: (...) =:: ...
(indeed, the loop
constructor returns not a value of the Circle
type, as it would be for an ordinary inductive type). Therefore, recursion with induction has to be determined manually: val recCirc = "rec_{S^1}" :: A ~>: a ~>: (a =:= a) ->: Circle ->: A val B = "B(_ : S^1)" :: Circle ->: Type val b = "b" :: B(base) val c = "c" :: Circle val indCirc = "ind_{S^1}" :: B ~>: b ~>: (( IdentityTyp.transport(B)(base)(base)(loop)(b) =:= b ) ->: c ~>: B(c) )
comp_base
and comp_loop
: val l = "l" :: ( IdentityTyp.transport(B)(base)(base)(loop)(b) =:= b ) val comp_base = "comp_base" :: B ~>: b ~>: l ~>: ( indCirc(B)(b)(l)(base) =:= b ) val P = "P(_ : A)" :: A ->: Type val f = "f" :: a ~>: P(a) val dep_map = "dep_map" :: A ~>: (P ~>: (f ~>: (a ~>: (a1 ~>: (( a =:= a1 ) ->: ( f(a) =:= f(a1) )))))) // dep_map IdentityTyp.extnslty(f), f val comp_loop = "comp_loop" :: B ~>: b ~>: l ~>: ( dep_map(Circle)(B)(indCirc(B)(b)(l))(base)(base)(loop) =:= l )
ProvingGround/mantle/target/web/classes/test
) directory.sbt server/run
command and then opening http: // localhost: 8080 in the browser.ProvingGround/core
subproject. To do this, put a new build.sbt here: ProvingGround/core/build.sbt
. import provingground._ import HoTT._ import TLImplicits._ import shapeless._ //import ammonite.ops._ import FansiShow._
Source: https://habr.com/ru/post/329176/
All Articles