
Thanks @sorawee I’ve added that to the wiki: https://github.com/racket/racket/wiki/Chromebook-Install\|https://github.com/racket/racket/wiki/Chromebook-Install

@liquidcloud9 has joined the channel

I have a question about point-free style with multi parameters.
Suppose I have a binary operator add
: (define (add x y)
(+ x y))
Now I want to define add3
. One way to achieve this purpose is: (define (add3 x y z)
(add x (add y z)))
But this is not point-free style. What I want is something like this: (define add3-with-point-free
(compose add (bimap identity add)))
Is this to be possible in Racket?

BTW, Haskell can do this kind of thing. For example: add :: (Int, Int) -> Int
add (x, y) = x + y
add3WithPointFree :: (Int, (Int, Int)) -> Int
add3WithPointFree = add . bimap id add
add3WithPointFree (1, (2, 3)) -- 6
The drawback of Haskell is that the function’s parameters are default currying, so the solution above is not very general. For example, we usually expect to call a function as following: add3WithPointFree 1 2 3
instead of add3WithPointFree (1, (2, 3))
Fortunately, Racket does not have this problem because the function’s parameters in Racket can be regarded as multi-values
.

I think @notjack’s https://docs.racket-lang.org/point-free/ might be helpful here?

You could certainlyy write those Haskell functions in Racket, but bimap
would be a bit weird

@sorawee I have tested his library, it seems not suitable here. (define add3-with-point-free
(compose add (join identity add))) ;; failed to compille.

it definitely compiles, but it doesn’t work

note that your Haskell code relies on the fact that (a,b,c) == (a,(b,c)) in haskell

without that, the code is much more awkward

@samth This because +
is a monoid, but if you want ((a,b),c)
, you can write: add . bimap add id

IMO, Racket is very suitable for bimap
or muti-map
, because the parameters of Racket are not curried by default. It can enhance composability.

what your bimap is doing is going from 3 values to 2, in a way that would be odd in racket

(bimap identity add) :: Int x Int x Int -> Int x Int

you can write that, but it would be very odd

(define ((bimap f g) a b c) (values (f a) (g b c)))

note that with that definition, (bimap add identity)
produces an error

@samth Yes, but it should work, right? There seems no ambiguity here.

no, I don’t think this version of bimap
makes any sense

3-tuples do not have a canonical bifunctor

I agree. The name bimap
is not good.

Maybe we can give it a new name.

But what should it do?

@samth May be something like (pseudocode): (define ((join2 f g) . args)
(let*-values ([(f-args args) (compute-args f args)]
[(g-args args) (compute-args g args)])
(values (apply f f-args) (apply g g-args))))

I’d rather do that pointfully I think

fancy-app
is about the most point freedom I can read, in my experience

(which would be why I haven’t worked on point-free
in a long time)


I just wrote a version of join2
: (define ((join2 f g fn) . args)
(let*-values ([(f-args g-args) (split-at args fn)])
(values (apply f f-args) (apply g g-args))) )
(define (add x y)
(+ x y))
(define add3-with-point-free
(compose add (join2 identity add 1))) ; <--- need fill 1 for identity
(define add3-with-point-free-2
(compose add (join2 add identity 2))) ; <--- need fill 2 for add
(add3-with-point-free 1 2 3) ;; 6
(add3-with-point-free-2 1 2 3) ;; 6
The drawback is it needs to fill in the number of parameters.

yup

I think that’s not a drawback, it’s an advantage

how many parameters something takes is useful information that I want to know when I’m reading code

the real advantage is it saves me from having to think up good names for them

I just installed fancy-app
. Why the example code failed in my REPL? (+ 1 (+ _ 2))
Told me ; +: contract violation
; expected: number?
; given: #<procedure:...ncy-app/main.rkt:28:27>
; argument position: 2nd
; other arguments...:
; 1

where did you find that example?


That example is to show how it doesn’t work in that case.

@samdphillips Oh, thanks.

It would be nice, if it can work in that case.

I think a plain lambda would be better there

It’s eventually about readability. (compose f g h)
conveys the intent better than (lambda (x) (f (g (h x))))
, so use it there. But if you need to perform gymnastic to flip argument order etc, no one is going to understand the code. It’s really not worth it (besides as a fun puzzle challenge)

to make that example work, you’d need some sort of delimiter to say where the the lambda goes. but at that point, it’s basically just a different syntax for lambda

drracket sometimes starts misbehaving? and i get a visual artifact where the -
(minus) character sometimes flashes or stays invisible, unless it’s a feature and i pressed a key combination by accident? edit: the font size was too small. i thought it was the same size from always, but i might have pressed the decrease font size twice. (although it had happened to me before i thought it was only decreased once)