Zulip Chat Archive
Stream: new members
Topic: Sum of `Type -> Type`
Nicolas Rolland (Nov 03 2023 at 10:24):
Are there standard constructor like Sum for Type -> Type
or for more general kinds ?
In Init/Core.lean we have
/--
`Sum α β`, or `α ⊕ β`, is the disjoint union of types `α` and `β`.
An element of `α ⊕ β` is either of the form `.inl a` where `a : α`,
or `.inr b` where `b : β`.
-/
inductive Sum (α : Type u) (β : Type v) where
/-- Left injection into the sum type `α ⊕ β`. If `a : α` then `.inl a : α ⊕ β`. -/
| inl (val : α) : Sum α β
/-- Right injection into the sum type `α ⊕ β`. If `b : β` then `.inr b : α ⊕ β`. -/
| inr (val : β) : Sum α β
@[inherit_doc] infixr:30 " ⊕ " => Sum
Alternatively is it better to go through categorical non sense : we can inherit a sum for Type ⥤ Type
through the monoidal properties of Type
(where are those derivations ?)
I am trying to recreate the classic higher order "toolkit" for defining data types, to define various folds as categorical constructions
Eric Wieser (Nov 03 2023 at 10:35):
I don't understand your question; are you looking for docs#Sigma ? For (Type -> Type) ⊕ (Type -> Type)
?
Nicolas Rolland (Nov 03 2023 at 14:09):
Eric Wieser said:
are you looking for docs#Sigma ? For
(Type -> Type) ⊕ (Type -> Type)
?
Not quite.
Going the Type -> Type
way, that would simply be
inductive Sum' (f : Type u -> Type x) (g : Type u -> Type w) (a : Type u) where
| inl' (val : f a) : Sum' f g a
| inr' (val : g a) : Sum' f g a
The ordinary Sum
being a particular case with constant f
mapping to a
and g
mapping to b
That can help to define say List a
as a least fix point of ListF r = ( ctt 1 ⊕ a * _ ) r
where we combine type constructors (and prove that they are functor later on)
But we can also look at the structure on the Type
category and combine functors type through the bunch of extra categorical properties of Type
, deriving the functor sum with
/--
When `C` is any category, and `D` is a monoidal category,
the functor category `C ⥤ D` has a natural pointwise monoidal structure,
where `(F ⊗ G).obj X = F.obj X ⊗ G.obj X`.
-/
instance functor_category_monoidal : monoidal_category (C ⥤ D) := ...
In that case ListF r = ( ctt 1 ⊕ a * _ ) r
would immediately be a functor Type ⥤ Type
I wonder if there's anything agreed upon on those line or should I do my own combinator library..
Eric Wieser (Nov 03 2023 at 14:47):
Your Sum' f g a
is the same thing as Sum (f a) (g a)
:
example (f : Type u -> Type x) (g : Type u -> Type w) (a : Type u) :
Sum' f g a ≃ Sum (f a) (g a) where
toFun
| .inl' l => .inl l
| .inr' l => .inr l
invFun
| .inl l => .inl' l
| .inr l => .inr' l
left_inv x := by cases x <;> rfl
right_inv x := by cases x <;> rfl
Nicolas Rolland (Nov 03 2023 at 17:16):
indeed. this pointwise business is very trivial. i would prefer to not define my own combinators
Eric Wieser (Nov 03 2023 at 17:20):
Is this sufficient for you?
def Function.Sum (f : Type u -> Type x) (g : Type u -> Type w) : Type u → Type _ :=
fun a => f a ⊕ g a
Nicolas Rolland (Nov 04 2023 at 07:58):
My question is : Is this Function.Sum in a standard library ?
My issue is code reuse, for me, and for other people who might use my stuff.
You can imagine if there was different versions of, say, Functor
, doing the same things. and people building on top of different versions.
Yaël Dillies (Nov 04 2023 at 07:59):
This is docs#Sum
Nicolas Rolland (Nov 04 2023 at 08:13):
Not quite, it's an indexed/pointwise version of docs#Sum.
This can be used as a "toolkit" to define data type.
For instance in haskell
newtype K a x = K a deriving Functor -- K for konstant
{- fmap _ (K a) = K a -}
newtype I x = I x deriving Functor -- I for identity
{- fmap k (I x) = I (k x) -}
newtype P f g x=P(f x,g x) deriving Functor --P for product
{- will give (Functor f, Functor g) => Functor (P f g), such that
fmap k (P (f x, g x)) = P (fmap k f x, fmap k g x) -}
newtype S f g x = S (Either (f x) (g x)) -- S for sum
instance (Functor f, Functor g) => Functor (S f g) where
fmap k (S (Left f x)) = S (Left (fmap k f x))
fmap k (S (Right g x)) = S (Right (fmap k g x))
allows to view integer binary tree as the least fixed point of the endofunctor
type TreeF = S (K Int) (P I I)
type Tree = Mu TreeF
etc..
Mario Carneiro (Nov 04 2023 at 08:17):
you will have difficulty defining least fixed point in that way
Mario Carneiro (Nov 04 2023 at 08:18):
more generally, lean does not promote "point free" style as much as haskell, plus it doesn't have as many restrictions regarding functions at the kind level, so if you want a lambda you can just use a lambda
Mario Carneiro (Nov 04 2023 at 08:20):
you could write TreeF
much more clearly as
def TreeF (A : Type) := Int ⊕ (A × A)
instead of writing combinatory logic
Nicolas Rolland (Nov 04 2023 at 08:21):
As a data type I think it would be unsafe inductive
, but I imagine I can Church encode it.
Good to keep in mind for the style.
Mario Carneiro (Nov 04 2023 at 08:23):
the more idiomatic way to write Tree
itself would be
inductive Tree where
| leaf : Int → Tree
| node : Tree → Tree → Tree
and you can use metaprogramming to prove that this type is the LFP of TreeF
Nicolas Rolland (Nov 04 2023 at 08:33):
Oh I see ! Can I then extract some TreeF
(and then build a fold) for this using metaprogramming ?
(I remember there is some trick to extract TreeF
from Tree
in Haskell)
Having combinators helps to derive the map of morphisms instead of manually writing them.
Maybe there is some clever way to get them back as well using metaprogramming ?
I imagine I should look at "metaprogramming in lean" for this LFP and the rest ?
Mario Carneiro (Nov 04 2023 at 08:38):
some of the details for this are worked out in our QPF paper but admittedly it's somewhat complicated to automatically extract functorial structure from arbitrary inductives
Nicolas Rolland (Nov 04 2023 at 11:45):
For reference to haskellers or otherwise,
To recover the underlying functor of a regular inductive data type in haskell, it pattern matches on the typed generic representation.
This Generic mechanism is part of the trusted core and is at work in deriving Generic
.
I imagine a similar mechanism could be part of a standard metaprogramming library in Lean ?
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Scratch where
import Data.Functor.Compose as C
import Data.Functor.Constant
import Data.Functor.Identity
import GHC.Generics as G
type family FG r a where
FG _ (U1) = Constant ()
FG r (K1 _ x) = Distrib r x
FG r ((:+:) f g) = ((:+:) (FG r f) (FG r g))
FG r ((:*:) f g) = ((:*:) (FG r f) (FG r g))
FG r (D1 _ k) = FG r k
FG r (M1 _ _ k) = FG r k
type family Distrib r f where
Distrib l l = Identity
Distrib r (f a) = Compose f (Distrib r a)
Distrib _ o = Constant o
data Rose a = Leaf | Rose a [Rose a]
deriving (Generic)
type ToFunc a = FG a (Rep a)
data Fix f = Fix (f (Fix f))
type Fix' f0 = Fix (ToFunc f0)
*Scratch> :kind! Fix' (Rose Int)
Fix' (Rose Int) :: *
= Fix (Constant () :+: (Constant Int :*: Compose [] Identity))
Last updated: Dec 20 2023 at 11:08 UTC