qi
qi copied to clipboard
Mixing fine-grained and blanket templates
Currently, _
and __
can be used independently but not together.
There are cases where it could make sense to use them together:
(~> ("a" "c" "d") (string-append _ "b" __))
It'd be worth exploring what it would take for this to work -- ideally it can be done while leveraging functionality provided by dependencies (such as fancy-app
), without having to reinvent it.
Also, _
can be used in the first (function) position, as of #58 . But __
cannot. Would it be worth supporting a case like this one: (~> (+ 1 2) (__ 3 4 5))
?
There are cases where it could make sense to use them together:
Yeah, it looks good to me!
But __ cannot. Would it be worth supporting a case like this one: (~> (+ 1 2) (__ 3 4 5))?
Well, I'm looking into whether >-
makes sense, I think (__ args ...)
would be useful, but not for matching function and arguments, it probably only matches functions.
Let me introduce the >-
in my mind -- it's similar to ==
, but splits the input according to the arguments (the functions' arity) :
(~> (1 2 3 4 5 6 7 8 9 10) ; 10 values
(>-
+ ; at least 0, match 0 values.
add1 ; 1
sub1 ; 1
- ; at least 1, match 8 values.
))
=> (values
0 ; (+)
2 ; (add1 1)
1 ; (sub1 2)
-46 ; (- 3 4 5 6 7 8 9 10)
)
So I guess __
in first position may work like >-
:
(~> (+ add1 sub1 -) (__ 1 2 3 4 5 6 7 8 9 10))
=> (values
0 ; (+)
2 ; (add1 1)
1 ; (sub1 2)
-46 ; (- 3 4 5 6 7 8 9 10)
)
And it makes sense in category theory: assume there are f : A -> B
and g : X -> Y
, then f * g
can be regarded as a function A * X -> B * Y
.
Disclaimer: this has nothing to do with the actual issue RE: _
and __
.
Let me introduce the
>-
in my mind -- it's similar to==
, but splits the input according to the arguments (the functions' arity) :(~> (1 2 3 4 5 6 7 8 9 10) ; 10 values (>- + ; at least 0, match 0 values. add1 ; 1 sub1 ; 1 - ; at least 1, match 8 values. )) => (values 0 ; (+) 2 ; (add1 1) 1 ; (sub1 2) -46 ; (- 3 4 5 6 7 8 9 10) )
This seems ambiguous to me: another satisfying assignment of inputs to flows would be
(+ 1 2 3 4 5 6 7)
(add1 8)
(sub1 9)
(- 10)
and there are 6 (?) more based on how many inputs are given to +
. There would need to be a rule about how to solve the constraints of multiple multi-arity flows to decide which inputs go where. And that sounds… well… hard.
Disclaimer: this has nothing to do with the actual issue RE: _ and __.
Oh, I've been working on how to use function values, and one strategy that came to my mind is the >-
I mentioned earlier. After seeing the __
in the first position, I felt that __
could also be used to use function values. And in order to illustrate this usage, I seem to have introduced some content that is not related to this issue. Do I need to open a new issue to discuss it?
There would need to be a rule about how to solve the constraints of multiple multi-arity flows to decide which inputs go where. And that sounds… well… hard.
Yes, how to decide the input is a question that has been confusing me. I tried to implement one with amb
:
(define (split-input n arity*)
(let loop ([n n] [arity* arity*] [res '()])
(cond
[(zero? n) (reverse res)]
[(null? arity*) (amb)]
[else
(define arity (car arity*))
(define m
(match arity
[(? natural?) arity]
[(? arity-at-least?) (pick-least n (list arity))]
[(? list?) (pick-least n arity)]))
(unless (>= n m) (amb))
(loop (- n m) (cdr arity*) (cons m res))])))
(define pick-least
(λ (n args)
(let loop ([args args])
(define arg (car args))
(match arg
[(? natural? m)
(unless (>= n m) (amb))
(amb m (loop (cdr args)))]
[(? arity-at-least?)
(let loop0 ([m (arity-at-least-value arg)])
(unless (>= n m) (amb))
(amb m (loop0 (add1 m))))]))))
(split-input 10 (map procedure-arity (list + add1 sub1 -))) ; '(0 1 1 8)
(split-input 10 (map procedure-arity (list + add1 sub1))) ; '(8 1 1)
Of course, using amb
is an inefficient approach, and I'm investigating whether there is a more efficient solution. But I want to point out that in general, >-
is unlikely to enumerate very large data, so it might make sense to implement it by amb
. And even if dealing with complex cases is necessary, using procedure-reduce-arity
can avoid a lot of overhead.
Discussing these in a purely theoretical way could be interesting, but in general I'd love to also see examples where proposed additions to the language would make some practical cases simpler or better in some way. And yes, this should be a separate issue 🙂
Ah, I don't have much experience with qi, but I thought something similar would be useful when I tried doing meru exercises. And when I was learning category theory I did see some constructs using f * g
as morphism (though I'm not sure how these constructs manifest in programming yet, I need some time to investigate further).
I need some time to sort out my previous thoughts, after that I will open a new issue. :)