compose in Typed Racket

59 views
Skip to first unread message

unlimitedscolobb

unread,
Dec 8, 2020, 6:00:47 PM12/8/20
to Racket Users
Hello,

I've found out that compose in Typed Racket has the type

(: compose (All (a b c) (-> (-> b c) (-> a b) (-> a c))))

which means that Typed Racket's compose can only combine two functions at a time.

In untyped code, I tend to use compose to combine more functions (e.g., 7), so I wrote myself the following definitions:

(: compose-n (All (a) (-> (-> a a) * (-> a a))))
(define (compose-n . funcs)
  (λ (x)
    (for/foldr ([x x]) ([f funcs])
      (f x))))

(: compose-3 (All (a b c d) (-> (-> c d) (-> b c) (-> a b) (-> a d))))
(define (compose-3 f1 f2 f3)
  (λ (x) (f1 (f2 (f3 x)))))

(: compose-4 (All (a b c d e) (-> (-> d e) (-> c d) (-> b c) (-> a b) (-> a e))))
(define (compose-4 f1 f2 f3 f4)
  (λ (x) (f1 (f2 (f3 (f4 x))))))

Is there a better way to chain compose calls in Typed Racket?

If the answer is no, is there any interest in including these three functions (as well as compose-5, 6, 7, 8) into Typed Racket?

-
Sergiu

Ben Greenman

unread,
Dec 9, 2020, 5:50:26 PM12/9/20
to unlimitedscolobb, Racket Users
> If the answer is no, is there any interest in including these three
> functions (as well as compose-5, 6, 7, 8) into Typed Racket?

I think these would be excellent in a package.

Someday later, perhaps poly dots and #:rest-star can combine to
improve the built-in type.

unlimitedscolobb

unread,
Dec 10, 2020, 1:16:16 AM12/10/20
to Racket Users
On Wednesday, December 9, 2020 at 11:50:26 PM UTC+1 Ben Greenman wrote:
> If the answer is no, is there any interest in including these three
> functions (as well as compose-5, 6, 7, 8) into Typed Racket?

I think these would be excellent in a package.

A package for compose-n and compose-3 to like 10 or 20?

I'm still not sure about how high the granularity of packages may get in Racket, but I'd like to share these functions, because they feel very useful to me.
 
Someday later, perhaps poly dots and #:rest-star can combine to
improve the built-in type.

From my naive viewpoint, I don't really see other natural ways of improving the type of compose other than what I wrote, the problem being that writing the type for arbitrary-arity composition would require specifying equality between the return type of every function and the argument type of the preceding one.  I'm not sure even Coq and Agda have that facility directly, certainly not Haskell or Idris to the best of my knowledge.  I don't expect them to go beyond binary compose, because it's sufficient to do any compositions.  It's that in Racket I find writing chains of nested compose calls somewhat clunky.

I'm not sure whether macros could be of use here.  I'll give it a think.

-
Sergiu

Hendrik Boom

unread,
Dec 10, 2020, 11:49:43 AM12/10/20
to Racket Users
Idea: Have a look at parendown https://docs.racket-lang.org/parendown/index.html

It would let you write
(compose f
(compose g
(compose h k)))
as
( compose f
#/ compose g
#/ compose h k
)

Which at least cuts down on the heavy indentation and parenthesis pile-up.

A macro might be able to generate either of the above from
(comp f g h k)
.

-- hendrik

>
> -
> Sergiu
>
> --
> You received this message because you are subscribed to the Google Groups "Racket Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to racket-users...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/d0eacc64-c11b-4edd-aa38-c62c15494c06n%40googlegroups.com.

unlimitedscolobb

unread,
Dec 10, 2020, 12:01:52 PM12/10/20
to Racket Users
On Thursday, December 10, 2020 at 5:49:43 PM UTC+1 hen...@topoi.pooq.com wrote:
On Wed, Dec 09, 2020 at 10:16:16PM -0800, unlimitedscolobb wrote:

> I'm not sure whether macros could be of use here. I'll give it a think.

Idea: Have a look at parendown https://docs.racket-lang.org/parendown/index.html

It would let you write
(compose f
(compose g
(compose h k)))
as
( compose f
#/ compose g
#/ compose h k
)

Which at least cuts down on the heavy indentation and parenthesis pile-up.

Interesting, thank you. I haven't yet tried parendown, but the more I see mentions of it the more likely I'm to try it out :-)
 
A macro might be able to generate either of the above from
(comp f g h k)
.
Indeed. I'm re-reading the docs on macros and I think I see a clean and clear way to achieve what I need.

I'll post my attempt as soon as I get the time to write it.

-
Sergiu

Ben Greenman

unread,
Dec 10, 2020, 3:51:50 PM12/10/20
to unlimitedscolobb, Racket Users
>> A package for compose-n and compose-3 to like 10 or 20?

Yes

I like the idea of _small packages that do one thing_ better than
_one-stop all-utility packages_ ... but do what you think makes sense.

>> Someday later, perhaps poly dots and #:rest-star can combine to
>> improve the built-in type.
>>
>
> From my naive viewpoint, I don't really see other natural ways of improving
> the type of compose other than what I wrote, the problem being that writing
> the type for arbitrary-arity composition would require specifying equality
> between the return type of every function and the argument type of the
> preceding one. I'm not sure even Coq and Agda have that facility directly,
> certainly not Haskell or Idris to the best of my knowledge. I don't expect
> them to go beyond binary compose, because it's sufficient to do any
> compositions. It's that in Racket I find writing chains of nested compose
> calls somewhat clunky.

Typed Racket already has some domain-specific ideas to support the
#:rest-star option. The equality-chaining constraint is definitely
new, but doesn't seem out of the question.

https://github.com/racket/typed-racket/blob/master/typed-racket-lib/typed-racket/rep/type-rep.rkt#L586-L612

I think other languages (Coq Agda Haskell Idris) have a harder time
here because they want to support currying. And even if they added
#:rest-star logic, their users might call it an anti-pattern because
it doesn't fit with partial application.

unlimitedscolobb

unread,
Dec 10, 2020, 4:23:05 PM12/10/20
to Racket Users
On Thursday, December 10, 2020 at 9:51:50 PM UTC+1 Ben Greenman wrote:
>> A package for compose-n and compose-3 to like 10 or 20?

Yes

I like the idea of _small packages that do one thing_ better than
_one-stop all-utility packages_ ... but do what you think makes sense.

Sounds reasonable to me, I'll create that package soon.

Thanks for the advice.
 
>> Someday later, perhaps poly dots and #:rest-star can combine to
>> improve the built-in type.
>>
>
> From my naive viewpoint, I don't really see other natural ways of improving
> the type of compose other than what I wrote, the problem being that writing
> the type for arbitrary-arity composition would require specifying equality
> between the return type of every function and the argument type of the
> preceding one. I'm not sure even Coq and Agda have that facility directly,
> certainly not Haskell or Idris to the best of my knowledge. I don't expect
> them to go beyond binary compose, because it's sufficient to do any
> compositions. It's that in Racket I find writing chains of nested compose
> calls somewhat clunky.

Typed Racket already has some domain-specific ideas to support the
#:rest-star option. The equality-chaining constraint is definitely
new, but doesn't seem out of the question.

https://github.com/racket/typed-racket/blob/master/typed-racket-lib/typed-racket/rep/type-rep.rkt#L586-L612

I see, #:rest-star seems quite powerful.  I'm curious to see what kind of stuff will come out of it.

I think other languages (Coq Agda Haskell Idris) have a harder time
here because they want to support currying. And even if they added
#:rest-star logic, their users might call it an anti-pattern because
it doesn't fit with partial application.

That's a good point.  I started forgetting how important implicit currying is in these languages.

-
Sergiu

unlimitedscolobb

unread,
Dec 16, 2020, 4:37:00 PM12/16/20
to Racket Users
Okay, so this is my shot:

#lang typed/racket

(require (for-syntax syntax/parse racket/match))

(define-syntax (multi-compose stx)
  (syntax-parse stx
    [(_ funcs:expr ...)
     (match-define (list fn fn-1 fs ...)
       (reverse (syntax->list #'(funcs ...))))
     (datum->syntax stx (for/fold ([sexp `(compose ,fn-1 ,fn)])
                                  ([f (in-list fs)])
                          `(compose ,f ,sexp)))]))

(multi-compose f1 f2 ... fn-1 fn) expands to (compose f1 (compose f2 ( ... (compose fn-1 fn) ... )))

My syntax-transformation-fu is essentially non-existent, even after reading through Greg's Fear of Macros multiple times, so please do tell me if you see some flagrant opportunities for improvement in the code above.

I'm planning to throw together a small package with compose-n , compose-3 to compose-10, and multi-compose, and publish it.  It would normally be my Christmas package for myself, but it make take some more time :-)

-
Sergiu

Sorawee Porncharoenwase

unread,
Dec 16, 2020, 5:03:49 PM12/16/20
to unlimitedscolobb, Racket Users

syntax-parse can already perform pattern matching. No need to use match

(define-syntax (multi-compose stx)
  (syntax-parse stx
    [(_ f:expr g:expr)
     #'(compose f g)]
    [(_ f:expr funcs:expr ...)
     #'(compose f (multi-compose funcs ...))]))

--
You received this message because you are subscribed to the Google Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to racket-users...@googlegroups.com.

unlimitedscolobb

unread,
Dec 16, 2020, 5:14:42 PM12/16/20
to Racket Users
Oh wow, that's impressively better than what I wrote!  I didn't know one could have *recursive* macros, to say nothing about the proper usage of syntax-parse.

Thank you very much for your quick answer!

-
Sergiu
Reply all
Reply to author
Forward
0 new messages