Is there any convenient way to get the name of the procedure. For example: (define (do-some-test func)
(printf "do-test: = ~v\n" func))
(do-some-test func)
This return >>do-test: = #<procedure:try-item>
It doesn’t looks nice, imo… I hope to get “try-item” directly.
@franci.dainese Great! Very thank!
If I have two values and I want to use a container to pack them, should I use cons
or list
? For example, (cons 1 2)
>> '(1 . 2)
(list 1 2)
>> '(1 2)
If values are primitive, cons
seems better than list
. Because cons
can only contain two values, it is more clear. In Haskell, it means a pair.
But if values are not primitive: (cons (list 1 2) (list 3 4))
>> '((1 2) 3 4) <--- it is bad.
(list (list 1 2) (list 3 4))
>> '((1 2) (3 4))
In this case, list
seems better.
But if you use list
, you can no longer use car
and cdr
to get the two projections. (car (cons (list 1 2) (list 3 4)))
(cdr (cons (list 1 2) (list 3 4)))
>> '(1 2)
>> '(3 4)
(car (list (list 1 2) (list 3 4)))
(cdr (list (list 1 2) (list 3 4)))
>> '(1 2)
>> '((3 4)) <---- it is bad.
So when we need a container to pack values, which constructor should I use? Is there something like Haskell’s pair or tuple in Racket? Thanks.
PS: There is another way, i.e. values
. But values
seems not be suitable as a container, because it cannot be nested.
Depends on your use case. Is this an “internal transfer of values”, then either would do (maybe use vector
). If the two values are to be used “externally” then I suggest making a struct to hold the two values.
If you use a list, you can use first
and second
instead of car
and cdr
.
Haskell’s pair
is the same as Racket cons
.
But Haskell’s pair is different from list.
In Racket (list 1 2)
is short for (cons 1 (cons 2 empty))
. I think Haskell works the same?
In Haskell, cons is just a constructor of List.
It is different from Pair.
I think first
and second
is good, thanks.
Maybe this is why Racket provide these two functions.
In Haskell cons
is :
and 1:2:[] is the same as [1,2,3]. In Racket it is (cons 1 (cons 2 empty)) and (list 1 2 3).
Yes, but what I want is Pair, not Cons. I means (,)
Since a list is build of pairs (cons-cells) the functions first and second are almost the same as (car xs) and (car (cdr xs)).
It seems Racket does not provide pair constructor. Racket mix list and pair.
I think a 2-element vector is what is closest to a Pair.
(I am so used to Racket - I didn’t get that Pair and Cons were different the first time I read your comment …)
Does vector can be pattern matching? and it seems no provide first
or second
function?
Pattern matching works for vectors. There is (vector-ref v 0)
and (vector-ref v 1)
. If you need to use them often, define little helper functions.
imo, if base-library does not provide this, then it means the pl designer does not recommend you to use it in that way.
This is why I think list
still is a good solution, because it provide me first
and second
. This may be to fix the mixing issue of cons and list.
I agree that a list is a good solution,
If you happen to need “tuples” at a later time, have vectors in mind.
Keep in mind that there’s a difference between your ideal pair
/fst
/snd
and list
/first
/second
. The first approach only needs to allocate two cells to hold references to both elements. The second approach needs to allocate four cells. Of course, this won’t matter if you don’t need very high performance program (and if you do need a very high performance program, Racket is probably a poor choice anyway).
cons
/car
/cdr
would be more similar to pair
/fst
/snd
in this sense. It allocates only two cells.
When you said “it is bad”, note that it’s just the issue with printing. The printing simply doesn’t satisfy your preference. But there’s nothing wrong per se.
@sorawee Why > The first approach only needs to allocate two cells to hold references to both elements. > The second approach needs to allocate four cells. What do you mean by cells?
This diagram shows (list 1 2 3)
:
It shows three pairs which each has two cells.
This shows (vector 1 2 3):
So in the above diagrams (list 1 2 3)
needs 6 cells, whereas (vector 1 2 3)
needs 3 cells (this is talking about cells due to the container itself)
(cons 1 2)
would be similar to (vector 1 2)
. They both use only 2 cells.
Thanks @sorawee. I know what you mean.
> When you said “it is bad”, note that it’s just the issue with printing. The printing simply doesn’t satisfy your preference. But there’s nothing wrong per se. (edited) Yes, that because Racket’s no provide a tag at the top of the cons
and list
. It would be nice, if Racket’s base library can provide some thing like (struct Pair (fst snd)
#:transparent)
If you prefer, you can install the rebellion
package and use https://docs.racket-lang.org/rebellion/Pairs.html instead.
If you’re not writing generic code, I also suggest making your own named types instead of using semantics-free containers like pairs. Tuple types (https://docs.racket-lang.org/rebellion/Tuple_Types.html) and record types (https://docs.racket-lang.org/rebellion/Record_Types.html) are good for that.
Is this a correct way to write imperative style code in racket? For example, (define (do-step-1)
(printf "do-step-1\n"))
(define (do-step-2)
(printf "do-step-2\n"))
(define (do-step-3)
(printf "do-step-3\n"))
(define (do-something)
(let ((_ (do-step-1))
(v (do-step-2))
(_ (do-step-3))
)
v))
(do-something)
But compile error: let: duplicate identifier
I must to rename the _
to __
to fix it.
If I have a multi-line statements, do I need to rename all? __
___
____
and so on.
Why don’t you just write:
(define (do-something)
(do-step-1)
(define v (do-step-2))
(do-step-3)
v)
As a bonus:
(define (do-something)
(do-step-1)
(begin0 (do-step-2) (do-step-3)))
will return the value of (do-step-2)
after evaluating both (do-step-2)
and (do-step-3)
Another way is to use let*
, which allows you to shadow existing variables
(define (do-something)
(let* ([_ (do-step-1)]
[v (do-step-2)]
[_ (do-step-3)])
v))
That means, racket do not hoisting internal define?
In some language, if you define (define (do-something)
(do-step-1)
(define v (do-step-2))
(do-step-3)
v)
That exactly means: (define (do-something)
(define v (do-step-2))
(do-step-1)
(do-step-3)
v)
Nope. It won’t be rewritten to the latter
Thanks @sorawee, i think i will use let*
solution. I usually use internal define
to define a function instead of value.
Sure, that works. Note that let*
makes your code drifted rightward a little bit, so I personally would prefer the first approach. The style guide also encourages the use of internal definition: https://docs.racket-lang.org/style/Choosing_the_Right_Construct.html#%28part._.Definitions%29
Alternative solution: (define (do-something)
(do-step-1)
(let ([v (do-step-2)])
(do-step-3)
v))
@sorawee Thanks, this guide is good!
But I still think let*
is better, because mutually recursive scope is unnecessary here. For example, in other imperative language, like C or Java func print-two (f)
print (first (f));
var f = rest (f); <-----this f in rest should be paramter f instead of var f
print (first (f));
var f = rest f;
return f;
end
It is more like let*
version.
Sure, in that case, let*
is the right construct to use.
I agree that rightward drift is a negative, but for me, semantics are more important than that, and I often want let
semantics over define
. Subjectively I prefer let
style also, but if I contribute to Racket code, I’ll endeavor to follow the guide :slightly_smiling_face:
When I find my code drifting “too far” to the right, it’s often a sign I need to refactor anyway.
Relevant: my proposal to bring let*
semantics into internal definition: https://github.com/racket/rhombus-brainstorming/issues/46
This feature is very suitable for debugging.
I have a question about null
and '()
, see this example: (define (test-match xs)
(match xs
[(list n1 null) (printf "case1 ~v\n" n1)] ;; <--------- see here I use null in pattern
[(list #f n2) (printf "case2 ~v\n" n2)]
[(list _ n3) (printf "case3 ~v\n" n3)]))
(test-match (list 99 null))
(test-match (list #f (list 1 2 3)))
(test-match (list 55 (list 1 2)))
>> case1 99
>> case1 #f
>> case1 55
When I use null
in the pattern, it match all 3 case. i.e. print “case1” 3 times? This not what I want.
But if I modify the code to: (define (test-match xs)
(match xs
[(list n1 '()) (printf "case1 ~v\n" n1)] ;; <--------- see here, now I use '() in pattern
[(list #f n2) (printf "case2 ~v\n" n2)]
[(list _ n3) (printf "case3 ~v\n" n3)]))
(test-match (list 99 null))
(test-match (list #f (list 1 2 3)))
(test-match (list 55 (list 1 2)))
>> case1 99
>> case2 '(1 2 3)
>> case3 '(1 2)
The result is correct.
Why? What’s the difference between null
and '()
? Can I use null
in pattern match? Thanks.
If you type null
in the repl, the result is printed as '()
.
So null
is an identifier bound to the value “the empty list”.
What’s the difference between null
and '()
?
In comparison '()
is literal syntax that evaluates to the empty list.
This implies that (eq? null '())
is true.
In short, null
is simply a name for ’().
So they are the same thing.
Why the behavior are different?
Sometimes it is convenient to use literal syntax sometimes it is nice to use an identifier.
But when to use literal syntax, and when to use identifier?
Most people use ’()
In HtDP they use empty
.
This is case, the behavior are completely different.
Same thing, but a better name than null
.
Ah!
That’s because case
doesn’t evaluate the left hand sides.
see https://docs.racket-lang.org/reference/pairs.html#%28def._%28%28quote._~23~25kernel%29._null%29%29 null
is also empty list.
In (case 42 [(null) "foo"])
42 will be compared to the symbol ’null
Null is not an empty list. It is an identifier, which is usually bound to the empty list.
Oh, I found that, null
is a bound variable in the pattern match
(define (test-match2 xs)
(match xs
[(list n1 null) (printf "case1 ~v ~v\n" n1 null)]
[(list #f n2) (printf "case2 ~v\n" n2)]
[(list _ n3) (printf "case3 ~v\n" n3)]))
(test-match2 (list 99 null))
(test-match2 (list #f (list 1 2 3)))
(test-match2 (list 55 (list 1 2)))
>> case1 99 '()
>> case1 #f '(1 2 3)
>> case1 55 '(1 2)
I can use null
as a variable…
Yes. In Racket (and Scheme) there are no reserved keywords.
null
be shadowed.
I understand, thanks @soegaard2 .
I think I should delete all null
in my code and use '()
instead. null
seems not useful.
I really like https://docs.racket-lang.org/debug/index.html for debugging. You don’t need to wrap parentheses or remove them. Just put #R
.
Also, I’m not aware of any language that rewrites in that way. It looks really unintuitive.
Languages like JS do have hoisting, but it does something like this:
(define (do-something)
(define v undefined)
(do-step-1)
(set! v (do-step-2))
(do-step-3)
v)
I think HtDP 2 switched to use '()
instead
please use define
instead of any of the let
variants, being explicit about whether you want mutually recursive or nested scopes is all pain for no gain
it has no optimization benefits and makes it harder to read the structure of control flow
@chansey97 when you want to pattern match on a constant like null
, you can also use the ==
pattern (match xs
[(== null) ...])
For the empty list I would just use '()
, but for other kinds of constants I use ==
.