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)