Zulip Chat Archive
Stream: lean4
Topic: lean-pipes: could someone help me finish?
Serhii Khoma (srghma) (Jun 09 2025 at 14:51):
hi, everyone. I have almost finished porting https://github.com/jwiegley/coq-pipes to lean
https://github.com/srhhma/lean-pipes/blob/main/Pipes/Basic.lean
Could someone help me?
How to prove that pushR terminates? (NOTE: pullR is similar)
TODO also (I think I can learn how to do it (with effort), but if someone wants to do it instead of me or give some advise - I will be very thankful):
Mario Carneiro (Jun 09 2025 at 22:06):
inductive ProxyPushRWF (a' a b' b c' c m r) where
| go : (b' → Proxy a' a b' b m r) → Proxy b' b c' c m r → ProxyPushRWF a' a b' b c' c m r
| reg : Proxy a' a b' b m r → ProxyPushRWF a' a b' b c' c m r
inductive ProxyPushRWFRel :
ProxyPushRWF a' a b' b c' c m r → ProxyPushRWF a' a b' b c' c m r → Prop where
| go_request : ProxyPushRWFRel (.reg (k xb')) (.go k (.Request xb' _kb))
| go_respond : ProxyPushRWFRel (.go k (kc' yc)) (.go k (.Respond xc kc'))
| go_m : ProxyPushRWFRel (.go k (kx x)) (.go k (.M mx kx))
| request : ProxyPushRWFRel (.reg (k a)) (.reg (.Request xa' k))
| m : ProxyPushRWFRel (.reg (k x)) (.reg (.M mx k))
| respond : ProxyPushRWFRel (.go k t) (.reg (.Respond xb k))
instance : WellFoundedRelation (ProxyPushRWF a' a b' b c' c m r) where
rel := ProxyPushRWFRel
wf := by
refine ⟨fun p => ?_⟩
have H1 (x k) (hk : ∀ y, Acc ProxyPushRWFRel (.reg (k y) : ProxyPushRWF a' a b' b c' c m r)) :
Acc ProxyPushRWFRel (.go k x : ProxyPushRWF a' a b' b c' c m r) := by
induction x with
| Request => exact ⟨_, fun | _, .go_request => hk _⟩
| Respond _ _ ih => exact ⟨_, fun | _, .go_respond => ih _⟩
| M _ _ ih => exact ⟨_, fun | _, .go_m => ih _⟩
| Pure => exact ⟨_, nofun⟩
have H2 (x) : Acc ProxyPushRWFRel (.reg x : ProxyPushRWF a' a b' b c' c m r) := by
induction x with
| Request _ _ ih => exact ⟨_, fun | _, .request => ih _⟩
| Respond _ _ ih => exact ⟨_, fun | _, .respond => H1 _ _ ih⟩
| M _ _ ih => exact ⟨_, fun | _, .m => ih _⟩
| Pure => exact ⟨_, nofun⟩
cases p with
| reg => exact H2 _
| go => exact H1 _ _ (fun _ => H2 _)
mutual
@[inline] def Proxy.pushR.go' [Inhabited r]
(fb : b → Proxy b' b c' c m r)
(k : b' → Proxy a' a b' b m r)
(p : Proxy b' b c' c m r)
: Proxy a' a c' c m r :=
match p with
| .Request xb' _kb => Proxy.pushR fb (k xb')
| .Respond xc kc' => .Respond xc (fun c' => Proxy.pushR.go' fb k (kc' c'))
| .M mx kx => .M mx (fun x => Proxy.pushR.go' fb k (kx x))
| .Pure xr => .Pure xr
termination_by ProxyPushRWF.go k p
decreasing_by all_goals constructor
@[inline] def Proxy.pushR [Inhabited r]
(fb : b → Proxy b' b c' c m r)
(p0 : Proxy a' a b' b m r) :
Proxy a' a c' c m r :=
match p0 with
| .Request xa' k => .Request xa' (fun a => Proxy.pushR fb (k a))
| .Respond xb k => Proxy.pushR.go' fb k (fb xb)
| .M mx k => .M mx (fun x => Proxy.pushR fb (k x))
| .Pure xr => .Pure xr
termination_by (.reg p0 : ProxyPushRWF a' a b' b c' c m r)
decreasing_by all_goals constructor
end
Mario Carneiro (Jun 09 2025 at 22:24):
and pull is indeed similar
inductive ProxyPullRWF (a' a b' b c' c m r) where
| go : (b → Proxy b' b c' c m r) → Proxy a' a b' b m r → ProxyPullRWF a' a b' b c' c m r
| reg : Proxy b' b c' c m r → ProxyPullRWF a' a b' b c' c m r
inductive ProxyPullRWFRel :
ProxyPullRWF a' a b' b c' c m r → ProxyPullRWF a' a b' b c' c m r → Prop where
| go_request : ProxyPullRWFRel (.go k (kc' yc)) (.go k (.Request xc kc'))
| go_respond : ProxyPullRWFRel (.reg (k xb')) (.go k (.Respond xb' _kb))
| go_m : ProxyPullRWFRel (.go k (kx x)) (.go k (.M mx kx))
| request : ProxyPullRWFRel (.go k t) (.reg (.Request xb k))
| respond : ProxyPullRWFRel (.reg (k a)) (.reg (.Respond xa' k))
| m : ProxyPullRWFRel (.reg (k x)) (.reg (.M mx k))
instance : WellFoundedRelation (ProxyPullRWF a' a b' b c' c m r) where
rel := ProxyPullRWFRel
wf :=
have H1 (x k) (hk : ∀ y, Acc ProxyPullRWFRel (.reg (k y) : ProxyPullRWF a' a b' b c' c m r)) :
Acc ProxyPullRWFRel (.go k x : ProxyPullRWF a' a b' b c' c m r) := by
induction x with
| Respond => exact ⟨_, fun | _, .go_respond => hk _⟩
| Request _ _ ih => exact ⟨_, fun | _, .go_request => ih _⟩
| M _ _ ih => exact ⟨_, fun | _, .go_m => ih _⟩
| Pure => exact ⟨_, nofun⟩
have H2 (x) : Acc ProxyPullRWFRel (.reg x : ProxyPullRWF a' a b' b c' c m r) := by
induction x with
| Respond _ _ ih => exact ⟨_, fun | _, .respond => ih _⟩
| Request _ _ ih => exact ⟨_, fun | _, .request => H1 _ _ ih⟩
| M _ _ ih => exact ⟨_, fun | _, .m => ih _⟩
| Pure => exact ⟨_, nofun⟩
⟨fun
| .reg _ => H2 _
| .go .. => H1 _ _ (fun _ => H2 _)⟩
mutual
@[inline] def Proxy.pullR.go' [Inhabited r]
(fb' : b' → Proxy a' a b' b m r)
(k : b → Proxy b' b c' c m r)
(p : Proxy a' a b' b m r) :
Proxy a' a c' c m r :=
match p with
| .Request xa' ka => .Request xa' (fun a => Proxy.pullR.go' fb' k (ka a))
| .Respond xb _kb' => (k xb).pullR fb'
| .M mx kx => .M mx (fun x => Proxy.pullR.go' fb' k (kx x))
| .Pure xr => .Pure xr
termination_by ProxyPullRWF.go k p
decreasing_by all_goals constructor
@[inline] def Proxy.pullR [Inhabited r]
(fb' : b' → Proxy a' a b' b m r)
(p0 : Proxy b' b c' c m r) :
Proxy a' a c' c m r :=
match p0 with
| .Request xb' k => Proxy.pullR.go' fb' k (fb' xb')
| .Respond xc k => .Respond xc (fun c' => (k c').pullR fb')
| .M mx k => .M mx (fun x => (k x).pullR fb')
| .Pure xr => .Pure xr
termination_by (.reg p0 : ProxyPullRWF a' a b' b c' c m r)
decreasing_by all_goals constructor
end
Serhii Khoma (srghma) (Jun 10 2025 at 07:44):
@Mario Carneiro big big thank You, magician. Merged
I have also tried to do next
--- DISALLOWED NEXT
@[inline]
def Proxy.next
[_root_.Pure m] [Bind m]
(p : Producer a m r) :
m (r ⊕ (a × (Producer a m r))) :=
match p with
| Proxy.Request v _ => False.elim v
| Proxy.Respond a fu => pure (Sum.inr (a, fun _ => fu ()))
| Proxy.M mx k => mx >>= fun x => Proxy.next (k x)
| Proxy.Pure r => pure (Sum.inl r)
that coq accepts (interesting why? is coq unsafe?)
In lean it is disallowed bc Proxy ... is u+1 and I cannot put it in m : u
I have tried other different ideas
and my favorite is idea 3
inductive ProxyNextStep.{u} (b : Type u) (m : Type u → Type u) (r : Type u) : Type (u+1)
| Respond {x : Type u} (downstreamOutput : Option b) (op : m x) (cont : x → ProxyNextStep b m r) : ProxyNextStep b m r
| Pure : r → ProxyNextStep b m r
@[inline] def Proxy.next [Monad m] (p : Producer b m r) : ProxyNextStep b m r :=
match p with
| Proxy.Request v _ => Empty.elim v
| Proxy.Respond b fu => ProxyNextStep.Respond (.some b) (pure ()) (fun _ => Proxy.next (fu ()))
| Proxy.M op cont => ProxyNextStep.Respond .none op (fun x => Proxy.next (cont x))
| Proxy.Pure r => ProxyNextStep.Pure r
termination_by structural p
but I'm still not sure it's the best. If someone has ideas - I will be very greatful.
Serhii Khoma (srghma) (Jun 25 2025 at 12:31):
Could someone help me to prove pushPullAssoc
theorem pushPullAssoc [Monad m] [LawfulMonad m]
(f : b' → Proxy a' a b' b m r)
(g : a → Proxy b' b c' c m r)
(h : c → Proxy c' c b' b m r) :
(f >+> g) >~> h = f >+> (g >~> h) := by
sorry
this is an exact copy of push_pull_assoc from coq-pipes
I tried, but failed
Could someone teach me?
Serhii Khoma (srghma) (Aug 30 2025 at 14:39):
UPDATE on lib: added concurrency primitives https://github.com/srghma/lean-pipes/blob/6fe222d7bb5fca97e084c44a67b4e700e341111a/pipes/Pipes/Concurrent/MergeProducers.lean#L265
Serhii Khoma (srghma) (Sep 01 2025 at 14:23):
@Mario Carneiro (or anyone) Could You help
-- this is ok
inductive Proxy.{u} (a' a b' b : Type u) (m : Type u → Type u) (r : Type u) : Type (u + 1)
| Request : a' → (a → Proxy a' a b' b m r) → Proxy a' a b' b m r
| Respond : b → (b' → Proxy a' a b' b m r) → Proxy a' a b' b m r
| M {x : Type u} (op : m x) (cont : x → Proxy a' a b' b m r) : Proxy a' a b' b m r
| Pure : r → Proxy a' a b' b m r
-- but I cannot write
#reduce IO (Proxy Unit Empty Empty Unit IO Nat)
-- bc of `: Type (u + 1)`. But I need `: Type u`
-- why I have it? bc of `M {x : Type u}`
-- Does anyone know some magic to allow me to write `IO (Proxy Unit Empty Empty Unit IO Nat)`?
Last updated: Dec 20 2025 at 21:32 UTC