You’re right that you need to learn about phase and procedural macros instead of pattern-matching macros. For example, on line 96 of “ffiREPL.rkt”, you want the for
(should be something like for/list
) to happen at expand time, not run time. So, you’ll need to use define-syntax
instead of define-syntax-rule
. Also, you’ll need to learn a little about scope and non-hygienic macros; the define-lib-func
introduced by the use
macro will never be visible, because it’s introduced by the macro (which means that only other code inroduced by the same macro invocation can see the binding).
@ben.knoble has joined the channel
Can anyone help me convert this to a foldl
/foldr
or for/fold
/for/foldr
: (define mapin (make-hash))
(define (make-thing2 lst)
(cond [(null? (cdr lst))
(define my-index 0)
(hash-set! mapin (car lst) my-index)
(add1 my-index)]
[else
(define my-index (make-thing2 (cdr lst)))
(hash-set! mapin (car lst) my-index)
(add1 my-index)]))
(make-thing2 '(d c b a))
(println mapin)
I am just scared of folding, but I know I can grasp it somehow
Does this do what you want? (for/hash ([x (reverse '(d c b a))]
[idx (in-naturals)])
(values x idx))
Folding is scary in the real world, but in functional programming we can fold forever (mostly). https://youtu.be/65Qzc3_NtGs
I think that’s it, thanks
Always use a paper that implements proper tail-call optimization when folding it!
I have actually actively been avoiding fold
and friends because I have built up no intuition on how to use it
If anyone has any good articles or blogs that explain it, let me know
I’ll try to look up some as well
The way I think about fold
is that it’s just a regular for
loop with functional update rather than imperative update
So let’s talk about functional update vs imperative update first.
In, say, Python, you might write:
x = 0
x = x + 1
x = x + 2
x = x + 3
This mutates the existing x
for each statement.
In functional programming, you don’t mutate existing variables.
(define x 0)
(define x* (+ x 1))
(define x** (+ x* 2))
(define x*** (+ x** 3))
You can express the above computation using for
sum = 0
for x in [1, 2, 3]:
sum += x
In functional programming, this could be similarly expressed using (for/)fold
.
(for/fold ([sum 0]) ([x '(1 2 3)])
(+ sum x))
@phanthero Though you could also write (let* ([x 0]
[x (+ x 1)]
[x (+ x 2)]
[x (+ x 3)])
....)
That’s called “shadowing”: each ‘new’ x uses the previous x in its definition, then shadows it.
There’s still no mutation involved, and the compiler will know how to optimize such cases.
Thank you @sorawee and @laurent.orseau, I think I understand it much better now!
@mflatt, thank you very much! I updated repo You mean I can’t make define-lib-func
globally visible even with non-hygienic macros?
I guess I’m in deep need of code review…
You know how there’s a second
that gives you the second element of a list? Is there something similar for a function that returns multiple values and you only want the second one?
(compose second list->values)
How does this work? And I don’t think list->values
is in the standard library.
@phanthero not sure if I understand your question. Do you mean, you want to implement ???
such that
(define (f)
(values 1 2))
(??? (f))
returns 2
?
Did I miss something? (I’ll admit I didn’t test it). True, that it isn’t in the standard library - but it is short definition using using call-with-values..
Oh… I did miss something. It’s values->list but that’s not a simple function.
Yeah, I think you will need a macro. A function won’t do it
Agree.
If that’s the case, the answer is no. You can’t solve this problem by writing a function
But you can write a macro
E.g.,
(define (f)
(values 1 2))
(define (extract-second* f)
(call-with-values f (λ (a b . whatever) b)))
(define-syntax-rule (extract-second x)
(extract-second* (λ () x)))
(extract-second (f))
I’ve been trying to use Racket this year for Advent of Code. However, I’m coming from Haskell, and I feel somewhat frustrated trying to code in a dynamic language.
I’m wondering what sort of things people do “in the real world” when writing Racket.
From briefly reading over the documentation, I can see a couple possibilities (although keep in mind I am a super beginner, so I definitely could have missed something):
- Use contracts. It looks like the standard library makes heavy use of contracts (which is pretty nice btw), but the section of the Racket guide on contracts suggests that same-module contracts (with
define/contract
) can be really slow, especially for functions called in a loop. From reading the guide, I get the feeling that it is discouragingdefine/contract
. Although when doing Advent of Code, I’d really want contracts on all (or most of) the functions in one module. - Use typed racket. I haven’t really looked into this. I don’t really have any idea of how popular typed racket is compared to normal racket. Do most people use typed racket when writing “actual code”? I haven’t gotten this impression from the Racket guide.
- Suck it up and write more tests. The
module+ test
thing makes it really easy to write tests basically inline, but I feel like the contract system or a type system would also be beneficial. There are tons of things that are easy for a type system to catch that are annoying to write tests for. - Don’t do anything. Just get used to working in a dynamic language. (I guess I don’t really consider this an option, but I’d be interested to hear from anyone that thinks this is the correct approach.)
As you are just doing Advent of Code I would recommend doing 1 and 3.
@samdphillips You’re suggesting that define/contract
isn’t slow enough to give me any sort of problem in the Advent-of-Code-style problems?
Would your suggestion be different if I was actually trying to use Racket in some sort of professional manner? (E.g. on a large project, with a big team, with some sort of performance goals, etc)
Another option is between 1 and 4. Write contract unofficially as a comment. There won’t be a check for you programmatically, but it serves as a documentation.
Yes and yes,
If you are using Racket professionally, you would probably write more tests and use contracts at major module boundaries. You may potentially move code to Typed Racket as the program evolves.
Oh, I guess “between 1 and 3” is more appropriate
Hmm, I hadn’t really considered this, but now that you mention it, I don’t know how helpful it would be in practice.
I don’t really feel like it makes it any “easier” to program, in the same way that a type system or contract system makes it “easier” to program.
Although I agree it is an improvement over just not doing anything!
@samdphillips Okay, that makes sense. So on a big project, more tests for things within modules, and then contracts for inter-module stuff.
At some point I’ll have to play around with typed racket, but for now I’ll probably just go with your suggestion of define/contract
and tests.
@sorawee Thanks, that is basically what I was imagining.
I guess the reason they used define
here instead of define/contract
is that they didn’t want to take the performance hit?
yeah
define/contract
could also be very bad especially for recursive functions, since it will check the contract for every (recursive) call.
Although you can write Typed Racket code from the beginning it really shines (IMO and by design) when you want to take an untyped Racket program and make it a bit more resilient.
Even worse, it could turn a tail recursion into a non tail recursion
That’s why contract is usually used at the module boundary
Ah, I see. Missing out on tail recursion could definitely be really bad.
Oh, huh, I guess I had thought that people would just write things from the beginning in Typed Racket.
@cdep.illabout_slack I’ve come from a typed language (mostly Scala, but Haskell and a few others as well) and been using Racket for ~6 months now.
At the beginning I was mimicking the Haskell/Scala code structures. They sort of (?) work, but feel really clunky.
I found myself resorting to macros a lot more - in a similar way that you’d define a tagless final or initial DSL; but the composition feels like it’s based on forms rather than types.
A really interesting example is something like https://docs.racket-lang.org/threading/index.html coming from Scala. I would have never thought to use a macro for it, but a newtype and a typeclass
There are a few observations which I found quite surprising though; I didn’t have too much of an issue inferring the types of variables within the function as much as I thought I would because of the naming scheme.
I think you should try making a simple example of what you’re trying to do, that doesn’t feature extra syntax or the ffi
define/contract doesn’t check on the recursive call
wat
you are absolutely right, wow.
what about mutual recursion?
I think we had this conversation yesterday or something, but define/contract is a contract between the function and the rest of the module
So other functions get the contracted version
Makes sense
This is a good description on how it can work in practice https://felleisen.org/matthias/manifesto/sec_full.html
It’s part of a larger paper about the design of Racket
Oh, OK, I think I confused this with Pyret lol
Thanks, I’ll check this out.
Thanks for the advice. I’ll have to check out the threading library to get a feel for how these types of things are done in Racket.