chansey97
2020-7-2 09:59:39

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.



chansey97
2020-7-2 10:04:40

@franci.dainese Great! Very thank!


chansey97
2020-7-2 12:41:15

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.


soegaard2
2020-7-2 12:43:49

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.


soegaard2
2020-7-2 12:44:30

Haskell’s pair is the same as Racket cons.


chansey97
2020-7-2 12:45:12

But Haskell’s pair is different from list.


soegaard2
2020-7-2 12:46:39

In Racket (list 1 2) is short for (cons 1 (cons 2 empty)). I think Haskell works the same?


chansey97
2020-7-2 12:47:13

In Haskell, cons is just a constructor of List.


chansey97
2020-7-2 12:47:28

It is different from Pair.


chansey97
2020-7-2 12:48:15

I think first and second is good, thanks.


chansey97
2020-7-2 12:49:09

Maybe this is why Racket provide these two functions.


soegaard2
2020-7-2 12:49:31

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).


chansey97
2020-7-2 12:50:16

Yes, but what I want is Pair, not Cons. I means (,)


soegaard2
2020-7-2 12:50:18

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)).


chansey97
2020-7-2 12:51:29

It seems Racket does not provide pair constructor. Racket mix list and pair.


soegaard2
2020-7-2 12:52:15

I think a 2-element vector is what is closest to a Pair.


soegaard2
2020-7-2 12:53:09

(I am so used to Racket - I didn’t get that Pair and Cons were different the first time I read your comment …)


chansey97
2020-7-2 12:54:16

Does vector can be pattern matching? and it seems no provide first or second function?


soegaard2
2020-7-2 12:55:07

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.


chansey97
2020-7-2 12:57:39

imo, if base-library does not provide this, then it means the pl designer does not recommend you to use it in that way.


chansey97
2020-7-2 12:58:41

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.


soegaard2
2020-7-2 13:01:04

I agree that a list is a good solution,


soegaard2
2020-7-2 13:01:29

If you happen to need “tuples” at a later time, have vectors in mind.


sorawee
2020-7-2 13:19:53

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).


sorawee
2020-7-2 13:20:22

cons/car/cdr would be more similar to pair/fst/snd in this sense. It allocates only two cells.


sorawee
2020-7-2 13:21:31

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.


chansey97
2020-7-2 13:23:04

@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?


soegaard2
2020-7-2 13:25:29

This diagram shows (list 1 2 3):


soegaard2
2020-7-2 13:25:53

It shows three pairs which each has two cells.


soegaard2
2020-7-2 13:26:17

This shows (vector 1 2 3):


sorawee
2020-7-2 13:27:06

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)


sorawee
2020-7-2 13:27:44

(cons 1 2) would be similar to (vector 1 2). They both use only 2 cells.


chansey97
2020-7-2 13:28:50

Thanks @sorawee. I know what you mean.


chansey97
2020-7-2 13:35:12

> 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)


sorawee
2020-7-2 13:52:16

If you prefer, you can install the rebellion package and use https://docs.racket-lang.org/rebellion/Pairs.html instead.


notjack
2020-7-2 14:50:23

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.


chansey97
2020-7-2 16:02:59

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.


sorawee
2020-7-2 16:11:12

Why don’t you just write:

(define (do-something) (do-step-1) (define v (do-step-2)) (do-step-3) v)


sorawee
2020-7-2 16:11:34

As a bonus:

(define (do-something) (do-step-1) (begin0 (do-step-2) (do-step-3)))


sorawee
2020-7-2 16:11:44

will return the value of (do-step-2) after evaluating both (do-step-2) and (do-step-3)


sorawee
2020-7-2 16:12:14

Another way is to use let*, which allows you to shadow existing variables


sorawee
2020-7-2 16:12:40

(define (do-something) (let* ([_ (do-step-1)] [v (do-step-2)] [_ (do-step-3)]) v))


chansey97
2020-7-2 16:13:14

That means, racket do not hoisting internal define?


chansey97
2020-7-2 16:14:28

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)


sorawee
2020-7-2 16:14:54

Nope. It won’t be rewritten to the latter


chansey97
2020-7-2 16:16:25

Thanks @sorawee, i think i will use let* solution. I usually use internal define to define a function instead of value.


sorawee
2020-7-2 16:20:04

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


wanpeebaw
2020-7-2 16:29:09

Alternative solution: (define (do-something) (do-step-1) (let ([v (do-step-2)]) (do-step-3) v))


chansey97
2020-7-2 16:39:27

@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.


sorawee
2020-7-2 16:40:14

Sure, in that case, let* is the right construct to use.


badkins
2020-7-2 17:10:39

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:


badkins
2020-7-2 17:12:08

When I find my code drifting “too far” to the right, it’s often a sign I need to refactor anyway.


sorawee
2020-7-2 17:19:46

Relevant: my proposal to bring let* semantics into internal definition: https://github.com/racket/rhombus-brainstorming/issues/46


chansey97
2020-7-2 18:03:21

This feature is very suitable for debugging.


chansey97
2020-7-2 18:51:14

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.


soegaard2
2020-7-2 18:52:26

If you type null in the repl, the result is printed as '().


soegaard2
2020-7-2 18:52:49

So null is an identifier bound to the value “the empty list”.


chansey97
2020-7-2 18:52:51

What’s the difference between null and '()?


soegaard2
2020-7-2 18:53:38

In comparison '() is literal syntax that evaluates to the empty list.


soegaard2
2020-7-2 18:54:27

This implies that (eq? null '()) is true.


soegaard2
2020-7-2 18:54:43

In short, null is simply a name for ’().


chansey97
2020-7-2 18:54:55

So they are the same thing.


chansey97
2020-7-2 18:55:09

Why the behavior are different?


soegaard2
2020-7-2 18:56:03

Sometimes it is convenient to use literal syntax sometimes it is nice to use an identifier.


chansey97
2020-7-2 18:56:31

But when to use literal syntax, and when to use identifier?


soegaard2
2020-7-2 18:56:45

Most people use ’()


soegaard2
2020-7-2 18:57:01

In HtDP they use empty.


chansey97
2020-7-2 18:57:07

This is case, the behavior are completely different.


soegaard2
2020-7-2 18:57:16

Same thing, but a better name than null.


soegaard2
2020-7-2 18:57:35

Ah!


soegaard2
2020-7-2 18:57:52

That’s because case doesn’t evaluate the left hand sides.



soegaard2
2020-7-2 18:58:25

In (case 42 [(null) "foo"]) 42 will be compared to the symbol ’null


soegaard2
2020-7-2 18:58:54

Null is not an empty list. It is an identifier, which is usually bound to the empty list.


chansey97
2020-7-2 19:01:22

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…


soegaard2
2020-7-2 19:02:00

Yes. In Racket (and Scheme) there are no reserved keywords.


chansey97
2020-7-2 19:02:14

null be shadowed.


chansey97
2020-7-2 19:02:54

I understand, thanks @soegaard2 .


chansey97
2020-7-2 19:05:05

I think I should delete all null in my code and use '() instead. null seems not useful.


sorawee
2020-7-2 22:40:35

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.


sorawee
2020-7-3 03:01:23

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)


sorawee
2020-7-3 03:04:30

I think HtDP 2 switched to use '() instead



notjack
2020-7-3 05:14:32

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


notjack
2020-7-3 05:15:04

it has no optimization benefits and makes it harder to read the structure of control flow


notjack
2020-7-3 05:20:12

@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 ==.