soegaard2
2020-12-21 09:50:16

It’s important to distinguish between the formal idea of how the computation is done and the actual implementation. Conceptually references to the values are passed around - in practise the important values gets special treatment (fixnums, the empty list, booleans). This is just to avoid anyone falling into the trap of thinking the implementation of function calls is inefficient.

There is a lot of choice when choosing the representation of values, but you can get an idea from Peter Bex’s blog post on the representation used in Chicken Scheme: https://www.more-magic.net/posts/internals-data-representation.html

https://www.more-magic.net/posts/internals-data-representation.html


joshibharathiramana
2020-12-21 10:04:32

I’m trying to understand how match works and the https://docs.racket-lang.org/reference/match.html#tocview_0:~:text=The%20last%20body%20in%20the%20matching,with%20respect%20to%20the%20match%20expression.\|reference says > The last _body_ in the matching clause is evaluated in tail position with respect to the https://docs.racket-lang.org/reference/match.html#%28form._%28%28lib._racket%2Fmatch..rkt%29._match%29%29\|match expression. Can someone please explain what this means? Does tail position here have to do something with tail recursion?


laurent.orseau
2020-12-21 10:08:01

Think of it as the expression calculating the return value


soegaard2
2020-12-21 10:08:12

Yes. It means a program like: (define (sum n [a 0]) (match n [0 0] [n (sum (- n 1) (+ n a))])) will run using constant stack space. The recursive call to sum in the body of the last match clause is syntactically in tail position, so at runtime the call will be tail recursive.


laurent.orseau
2020-12-21 10:08:19

(which could be a recursive call, yes)


joshibharathiramana
2020-12-21 10:12:10

hmm so does that means match itself is tail recursive? i.e. if the stack blows up, it is the fault of some clause , but not match itself? Is my understanding correct?


soegaard2
2020-12-21 10:12:56

Yes.


phanthero
2020-12-21 10:41:03

Hmm, how come streams take constant space?


anything
2020-12-21 13:11:53

A stream is a sequence of elements like a list is. However, in a stream the rest of list has not been computed yet, so it doesn’t take the space. A list is fully computed and so you must store each element of the list somewhere in memory. That’s why streams let you build “infinite sequences”. This wouldn’t be possible if you had to store each element in memory.


xuxue
2020-12-21 14:18:34

@xuxue has joined the channel


xuxue
2020-12-21 14:21:24

> (define \foo "bar") > foo "bar" Hi, I guess Racket would treat identifiers start with \ the same with the one without \, right?


xuxue
2020-12-21 14:22:52

I’m not sure whether is it safe to use them interchangeably


hoshom
2020-12-21 14:28:19

How come modulo only works on integers?


hoshom
2020-12-21 14:29:04

In python I can do 1.5%5 for example


laurent.orseau
2020-12-21 14:32:43

My guess is it’s because "" is an escape character in strings (and symbols share a lot with strings). "\f" corresponds to no special escape sequence, so it is just interpreted as “f”. Just my guess though


ben.knoble
2020-12-21 17:42:10

Doesn’t look like http://codereview.stackexchange.com\|codereview.stackexchange.com has a very active racket community. I wrote my first macro while trying to understand for and wanted some feedback; where’s a good place for that?


laurent.orseau
2020-12-21 17:52:31

#general probably


laurent.orseau
2020-12-21 17:56:02

Should be relatively easy to write this one based on the builtin one. Remember to check negative numbers, exact quotients, zeros, and +inf.0 and +nan.0


laurent.orseau
2020-12-21 17:56:33

But possibly it already exists in some library. @soegaard2?


soegaard2
2020-12-21 18:23:29

I thought we had an flmod somewhere, but I can’t it. How is it defined? Is 1.5 % 5 the same as (15%50)/10 ?


soegaard2
2020-12-21 18:44:25

According to Stackoverflow:


soegaard2
2020-12-21 18:45:18

(define (flmod x m) (- x (* (floor (/ x m)) m))



laurent.orseau
2020-12-21 19:18:37

joshibharathiramana
2020-12-21 19:50:41

I feel stupid even asking this, but I’ve been staring at this code for past 15 minutes and can’t figure out what I’m doing wrong > (letrec ([my-even? (lambda (x) (if (zero? x) #t (not (my-odd? (- x 1)))))] [my-odd? (lambda (x) (if (zero? x) #f (not (my-even? (- x 1)))))]) (my-even? 5)) #t can someone point out what the bug is?


ben.knoble
2020-12-21 19:52:32

remove the nots: x is even iff (x–1) is odd


ben.knoble
2020-12-21 19:52:40

and vice-versa


joshibharathiramana
2020-12-21 19:53:07

oh yeah dang how could I have even written that :face_palm:


ben.knoble
2020-12-21 19:53:54

Happens :upside_down_face:


phanthero
2020-12-21 21:46:23

Could you provide an example? In the past, I asked about how to use streams like ports to read in the preorder traversal of a tree, and I saw a great example by @sorawee as to how to use streams for functional programming. Are you saying that the stream (stream '(bob 2) '(rob 1) '(job 0) '(foo 0)) was not computed all at once in this example?

https://racket.slack.com/archives/C09L257PY/p1605678504289300


phanthero
2020-12-21 21:47:13

Not really sure how streams are implemented, but in this particular example, they at least seem to be computed all at once?


phanthero
2020-12-21 21:47:43

Also, how could one create “infinite” streams?


phanthero
2020-12-21 21:48:06

One wouldn’t be able to type them out like in this example …


phanthero
2020-12-21 21:48:12

Hmmmmmmmmmm


me1890
2020-12-21 21:48:44

Streams like that do get computed all at once, but the real advantage is in defining your own streams with gen:stream


anything
2020-12-21 21:51:05

This lecture will give you an exact and step by step construction of a stream. (The entire course is really great — and really famous.) https://www.youtube.com/watch?v=a2Qt9uxhNSM


me1890
2020-12-21 21:54:04

Here’s a small example stream in racket too: (require racket/stream) (struct doubles (n) #:methods gen:stream [(define (stream-empty? stream) #f) (define (stream-first stream) (doubles-n stream)) (define (stream-rest stream) (doubles (* 2 (doubles-n stream))))]) (for ([x (stream-take (doubles 1) 10)]) (displayln x))


phanthero
2020-12-21 21:54:28

Man, I wish I went to MIT lol


phanthero
2020-12-21 21:55:06

Ah, I didn’t know about gen:stream at all. No wonder I was confused


anything
2020-12-21 21:55:27

Hm, I don’t know if going to MIT is too good of a thing. But I’m really happy to be able to read what they write and understand what they say. Because, indeed, a lot of good work has been done there for sure.


anything
2020-12-21 21:58:03

Don’t punish yourself. You’re probably so concentrated on the syntax that you forgot to consider the strategy itself. Once the syntax is very familiar to you, you’ll get back to the strategy. (Loved the icon!) :sun_with_face:


anything
2020-12-21 22:05:01

Yes, an infinite stream wouldn’t be typed out. We’d give a stream-constructor a rule to go on. Start with this and the next is given by some other rule. If this rule has no end — like “a natural number plus one” —, the stream becomes infinite. And there are ways to do this with the stuff you already know — lists, functions, lambda expressions. In that lecture, you’ll watch it all from scratch. And you’ll probably watch other lectures too and the entire course is really worth watching carefully. And if you get done with the course, reading the book will also be an amazing step and perhaps you’d intermix both things. (But if you haven’t read yet, HtDP should definitely be the first book. Perhaps even before watching this course completely. HtDP has changed my life. This book has a very deep message and it’s not easily grasped in the first sections. I read it with the objective of just acquiring some local vocabulary — I was having a hard time with Racket’s documentation. But by reading it, I realized the book was teaching me how to program and I didn’t know I didn’t know. So it was life changing. But after HtDP, Structure and Interpretation of Computer Programs is definitely a new challenge. But I do think that watching the course before reading the book is a good idea. Perhaps better still would be watching the lectures and reading the book, then continuing. Life is also full of steps forward and backwards and redesigning. Your best luck though is that you’re among people who love this stuff and they are exceptionally talented. I’m not one of them, for sure, but they are here everywhere.


joshibharathiramana
2020-12-21 22:18:22

@anything I was actually using this to test a metacircular interpreter I was writing so I was getting frustrated and thought the fault was with the interpreter :sweat_smile:


joshibharathiramana
2020-12-21 22:18:45

Thanks for the compliment :smile:


anything
2020-12-21 22:23:52

It seems you’re [not] using paredit-mode. I love it.


phanthero
2020-12-21 22:56:29

> Your best luck though is that you’re among people who love this stuff and they are exceptionally talented. I’m not one of them, for sure, but they are here everywhere Given that we are both here doing and discussing this stuff, even though we might not be exceptionally talented, I wager that we both love this stuff, even though we are not as talented as the people who made Racket and made it as big as it is today.

I was putting off HtDP since I was like “uhhhhh, I already know Racket…. reading books is long and painful”, but I think I understand what you mean now. I will definitely give it a go after I watch the lecture, when I have the time (I know I need to make the time). The next few months, I will be working with Haskell, which is supposedly a “pure” functional programming language, but I feel that the lessons from this lecture and HtDP will obviously apply there as well.

At the end of the day we are all suffering from the Dunning Kruger effect. Most human beings (including me) are not really experts at anything, but people tend to think they know more than they do, which I like to avoid. Thank you for your recommendations, Any. I certainly need to rethink my approach to learning. And yes, I am glad to have discovered the Racket community. They weren’t joking about “Vibrant Community”


anything
2020-12-21 23:02:12

Let me also say that it’s good to go with your intuition as well. If you think a book is not for you, might as well respect that. But perhaps like me, you had the impression that the book is kinda trivial, which it is not. It doesn’t seem a crime to also jump ahead in a book to see if it’s still all trivial and perhaps be humble to go back when we find it is not. HtDP isn’t really about Racket. It’s perhaps more about processes and it is definitely about a certain way of thinking that will definitely help you. But even it would turn out to be trivial, what’s the problem? Who hasn’t ever read a trivial book or a bad book? Sometimes bad books give us immense insight too. Sometimes a bad book leads into errors and then I spot the error and I try to clear it up and insight comes. I must thank the book for the error. A bad book is only bad for the lazy. But of course one shouldn’t read all bad books in the world because we’d end up just doing that. In a sentence, respect to your intuition and stay in control of your education.


soegaard2
2020-12-21 23:04:53

I think I tried it at some point - but couldn’t get used to it.


soegaard2
2020-12-21 23:05:29

I am happy with paren-mode.


anything
2020-12-21 23:08:33

I failed at first too. These days I only use two commands — those that bring stuff from the right into the current parentheses I’m in and those that shift stuff out of the parenthesis I’m in. (And this seems very useful in itself.) I also had to remap C-<right> and C-<left> because by default those do the operations above and I’m too used to use them to just navigate from s-expr to s-expr. I remapped those operations to M-/ (for enlarging the scope of my current parentheses [using here new words to describe the operation I described earler] and M- for the inverse operation. Just that made me love it. (Oh, I also use M-s to get rid of a parentheses. That’s basically what all I use. I try not to spend too much time learning to use tools because there’s too many ou there. Every once in a while, though, we feel we need to improve.)


anything
2020-12-21 23:09:31

I’m using paredit-mode even in the Racket REPL, where I think it’s also very useful.


anything
2020-12-21 23:10:32

Here’s basically all I do wrt paredit: ;; Please, autoload racket-mode (autoload 'racket-mode "racket-mode" "My Racket REPL." t) ;; When we load racket-mode by Greg Hendershott we also enable ;; paredit-mode because that mode is so great. However, paredit-mode ;; doesn't work when we are in noweb-mode. So let's not turn it on ;; when we are in literate programming worlds --- our daily world. (defun turn-on-paredit-only-outside-noweb-mode () (when (not (bound-and-true-p noweb-mode)) (paredit-mode)) t) (add-hook 'racket-mode-hook 'turn-on-paredit-only-outside-noweb-mode) (add-hook 'racket-repl-mode-hook 'paredit-mode) ;; Let's set up some handy keybindings (eval-after-load 'paredit '(progn (define-key paredit-mode-map (kbd "&lt;C-left&gt;") 'paredit-backward) (define-key paredit-mode-map (kbd "&lt;C-right&gt;") 'paredit-forward) (define-key paredit-mode-map (kbd "M-/") 'paredit-forward-slurp-sexp) (define-key paredit-mode-map (kbd "M-\\") 'paredit-forward-barf-sexp))) (eval-after-load 'racket-repl '(progn ;; In the REPL, I don't really ask for indentation via &lt;tab&gt;. At ;; the most, I'd press RET for indentation. So I would prefer ;; &lt;tab&gt; to expand names. (define-key racket-repl-mode-map (kbd "&lt;tab&gt;") 'dabbrev-expand))) ;; Smart parentheses (show-paren-mode) (electric-pair-mode) (autoload 'enable-paredit-mode "paredit" "Turn on pseudo-structural editing of Lisp code." t) (add-hook 'emacs-lisp-mode-hook 'enable-paredit-mode) (add-hook 'lisp-mode-hook 'enable-paredit-mode) (add-hook 'scheme-mode-hook 'enable-paredit-mode)


phanthero
2020-12-21 23:12:09

Yeah, even certain theorems in mathematics that seem “trivial” lead to extraordinary outcomes and uses in reality, something like prime numbers and finding them for example, is very useful, even though it just seems trivial (it isn’t really anymore). Trivial things lead to more complex things, and my impression is that HtDP does just this. I was wondering why these books have such weird names: • How to Design Programs • Structure and Interpretation of Computer Programs When I just thought these were just guides and references on Racket and Scheme (so why don’t they call them that?), but they seem to be much more, from what I have heard from others (haven’t read either of the above). Yeah… I am probably wasting too much time in my life doing meaningless things like surfing facebook or something, but this is probably a good way to spend the holidays. Thank you for your insight, it makes a lot of sense


anything
2020-12-21 23:12:52

Paredit-mode is a problem if you’re a literate-programming user. I use a workaround for that, but that’s all irrelevant to you if you don’t edit Lisp together with non-Lisp in the same document.


anything
2020-12-21 23:15:17

Like Bertrand Russell would probably say — every once in a while it’s a good idea to put a question mark on those things we take for granted. I think you just did that with how you spend your time. :slightly_smiling_face: You’re welcome. Thanks for the conversation! I hope you enjoyed as much as I did.


phanthero
2020-12-21 23:28:25

That reminds me, a few months ago I bought Bertrand Russell’s autobiography from the local library’s book sale, but haven’t gotten around to reading it, LOL. I’ve just stopped reading books. I need to start again. Yes, I really enjoyed the conversation :slightly_smiling_face:


phanthero
2020-12-22 00:51:34

Sorry, going through some older logs a bit, and I came across this example from sorawee of the dot notation.

So, this portion of the documentation sort of explains what’s happening: https://docs.racket-lang.org/reference/reader.html?q=dot#%28part._parse-pair%29

But I cannot understand these paragraphs, even though I have read over them several times: > If the reader finds two data between the matching parentheses that are separated by a delimited ., then it creates a pair. More generally, if it finds two or more data where the last datum is preceded by a delimited ., then it constructs nested pairs: the next-to-last element is paired with the last, then the third-to-last datum is paired with that pair, and so on. > > If the reader finds three or more data between the matching parentheses, and if a pair of delimited .s surrounds any other than the first and last elements, the result is a list containing the element surrounded by .s as the first element, followed by the others in the read order. This convention supports a kind of infix notation at the reader level. Especially things with 2 or more infix dots is confusing the heck out of me.. At the end, this is also stated: > If a delimited . appears in any other configuration, then the https://docs.racket-lang.org/reference/exns.html?q=dot#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._exn~3afail~3aread%29%29\|exn:fail:read exception is raised. Similarly, if the reader encounters a ), ], or } that does not end a list being parsed, then the https://docs.racket-lang.org/reference/exns.html?q=dot#%28def._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._exn~3afail~3aread%29%29\|exn:fail:read exception is raised. I guess this paragraph I don’t understand because I am unclear on what configurations of dot is allowed and not allowed.

For example, I don’t really see why this is allowed, or how it would be represented in the usual cons or list notation: '(foo a b . res) Also, why is (a . b . c) equivalent to '(b a c)? Is there any particular reason this happens?

Not sure if the documentation prohibits more than two infix dots, but any more than 2 do raise errors it seems. Another question for me is what happens when there are multiple things between/before/after the dots (both in the case of using 2 dots, and also the case of using one single infix dot) Eg. '(foo bar . quux baar . baar ba b) Eg. '(fooo asdfdasf . racket is great) Sorry if this is a confusing question..


samdphillips
2020-12-22 01:02:08

I’m pretty sure it’s in the docs (can’t look it up right now) but it is so you can do (num1 . &lt; . num2) or related infixy things


phanthero
2020-12-22 01:36:50

Hmm, I might have put too much stuff into a single question. A simpler and shorter question is why does '(1 . 2 . 3) produce '(2 1 3)? Any particular reason?


samth
2020-12-22 01:42:16

@samdphillips gave the answer


samdphillips
2020-12-22 01:43:05

Another part of the reason is that quoted data is read the same way as code.


phanthero
2020-12-22 01:46:08

I’m not sure what you mean by quoted data is read the same way as code… hmm.. how would this motivate this type of infix notation?


phanthero
2020-12-22 01:52:39

Further, notation such as (num1 . &lt; . num2 . &lt; . num3) doesn’t work, so I guess we are limited to just 2 dots then.


samth
2020-12-22 01:53:15

The motivation is so you can write (a . &lt; . b) instead of (&lt; a b)


sorawee
2020-12-22 01:54:26

And yes, it’s limited to only two dots


phanthero
2020-12-22 01:57:52

Thanks!


phanthero
2020-12-22 01:58:12

The next question is how to represent '(foo a b c d . res) is list or cons notation


phanthero
2020-12-22 01:58:15

Can this be done?


phanthero
2020-12-22 01:58:19

This seems to be valid Racket code


phanthero
2020-12-22 01:58:31

But not sure how to convert to cons


sorawee
2020-12-22 01:58:48

(cons 'foo (cons 'a (cons 'b (cons 'c (cons 'd 'res)))))


sorawee
2020-12-22 01:59:01

It’s not a list


phanthero
2020-12-22 01:59:03

So it is an improper list


phanthero
2020-12-22 01:59:05

I see


phanthero
2020-12-22 01:59:59

I was going to ask how to represent this in cons: '(foo a b . c d) but that seems to be invalid Racket code


phanthero
2020-12-22 02:00:16

Only one thing after the dot then, I see


phanthero
2020-12-22 02:04:19

I understand this perfectly now: > More generally, if it finds two or more data where the last datum is preceded by a delimited ., then it constructs nested pairs: the next-to-last element is paired with the last, then the third-to-last datum is paired with that pair, and so on.


phanthero
2020-12-22 02:06:23

@plragde was saying that dotted pairs are a historical artifact to save one word of memory? Is this true? Was this introduced in the original LISP 1 then? Not entirely sure why it would save one word of memory… hmmmm


phanthero
2020-12-22 02:07:17

I can’t find any documentation on it being used in previous LISPs



phanthero
2020-12-22 02:09:08

Doesn’t explain the saving of the word of memory, but demonstrates existence before Racket. huh


phanthero
2020-12-22 03:28:03

Wait, one thing that still doesn’t make sense, if we are passing racket_value*, can’t we do *racket_value and modify the underlying value in the function itself? Is this somehow prevented in C, or some error raised? Sending pointers into a function obviously means you can dereference and modify the underlying value.. This means: (define (foo a) ;; dereference a and modify the original value, since a is technically a pointer ... ) (define x 42) (foo x) foo will have changed x ’s value, right? Is this prevented somehow in the C code? @rokitna


rokitna
2020-12-22 04:38:17

I’m confused about about what you mean when you say “sharing stacks.” Isn’t there usually just one call stack anyway (unless threads or continuations or coroutines are in the mix)? The way I understand C-like architectures, when you call a function, you push a frame onto the stack to keep track of what address to jump back to. It’s still the same stack, just with another frame on it.

As for whether you can write { *v = compute_new_v(); } when v is a (racket_value *)… the thing is, Racket doesn’t have any operation that does that, at least in typical code. Maybe it’s possible to do that from the C side or by using something in the unsafe modules. (As @soegaard2 says, some small and common immutable values such as fixnums are passed around directly rather than using pointers in practice, so even if there’s a way to do this for the ones that are pointers, it’s not going to work for those other ones.)


hoshom
2020-12-22 05:16:32

Neat, thanks! Might be a good idea to add this to standard library. At the time I asked, I wrote it by subtracting in a loop :smile:


phanthero
2020-12-22 05:22:12

Not sure what fixnums are (are these a construct in C? I looked up “fixnums in C” but I only got search results about things in Ruby, maybe this is the union you were referring to before?)

Yeah, sorry about the confusion with the stack, you are correct. I recently wrote a compiler for a basic subset of C (in Racket) so I understand what you mean, yeah. I should have said “sharing frames on the stack”, I meant “frame” instead of stack.

Ah, that makes sense, I was just misunderstanding, yeah. Although, if only the pointer to the variable ( racket_value*) is passed, how does the called function in Racket know the racket_value? Wouldn’t this involve dereferencing? Does Racket then somehow implement “read-only” dereferencing? Oh, maybe a const pointer?


rokitna
2020-12-22 05:23:38

The reason (a b c . func . d e f) is equivalent to (func a b c d e f) is just to allow people to write certain notations like (a . -&gt; . b) and (a . &lt; . b) in arrangements that are a little more familiar, rather than having to write (-&gt; a b) and (&lt; a b).


rokitna
2020-12-22 05:28:11

(foo a b . res) is an “improper list” (a series of cons cells that ends with something other than null). Every non-dotted list (a b c) is shorthand for (a . (b . (c . ()))) where (a . b) corresponds roughly to (cons a b) and () corresponds to null. To save on parens, multiple nested cons cells can be merged together like (a b c . ()), and to make proper lists easier to write, that last . () can be omitted.


rokitna
2020-12-22 05:30:24

This dotted notation for cons cells has been around for as long as s-expressions have, I think. However, I think the two-dot notation for infix is something that’s novel to Racket.


rokitna
2020-12-22 05:34:19

Ah, you already got a bunch of these answers in the main thread. XD lol


phanthero
2020-12-22 05:39:27

Yeah, I made this question way too long for no particular reason, sorry you had to read that wall of text


rokitna
2020-12-22 05:44:27

Sometimes I hear people concerned with trying to allocate as little as possible, for performance (especially in historical environments with slow garbage collectors and limited memory). Some Lisp and Scheme programmers like to model their data structures in terms of cons cells and symbols (sometimes just like s-expressions, and sometimes more like binary trees), so the cons operation is a frequent source of allocations when operating on that data. Building an improper list saves on one cons call compared to a proper list.


rokitna
2020-12-22 05:44:55

No problem! :stuck_out_tongue:


rokitna
2020-12-22 06:00:22

In a sense, there isn’t even read-only dereferencing. In this allegory, all Racket expressions and function arguments are of type (racket_value *). If we add a dereferencing operation to the language, it would return something of type racket_value, which would be useless since what everything wants is still a (racket_value *).

In that case, when do you ever actually interact with the data? In between. The basic operations take a pointer, dereference it, compute with that data, and return another pointer.


rokitna
2020-12-22 06:14:19

Lisp dialects, including Scheme and Racket, typically have arbitrary-precision numbers. As long as their binary encodings are small enough to fit in memory, you can generate and work with large numbers (like factorials). But it’s still the case that arithmetic on smaller numbers, which more directly corresponds to CPU instructions, is more likely to be fast. Fixnums (fixed-precision numbers) are the numbers small enough to have meaningfully optimized arithmetic.

Fixnums are contrasted to to bignums (arbitrary-precision numbers). You might also come across fixints and bigints, the integer-specific versions of these terms.


phanthero
2020-12-22 06:37:13

Thanks! > In that case, when do you ever actually interact with the data? In between. The basic operations take a pointer, dereference it, compute with that data, and return another pointer. So dereferencing does happen somewhere then? I am just wondering where this dereferencing happens. Say we call (foo a) where the value of a is 1. Now, in the function signature of foo, we have (foo x). So the original location (pointer) of a is still stored in the stack frame of the caller, but the callee (foo) must create a location (pointer) for x, where it stores *a. So does this dereferencing happen during before the function call, or after the function call? How is it possible that we totally hide the underlying racket_value, is what I am kind of getting at (maybe?)


rokitna
2020-12-22 07:55:25

I’m not sure I follow. If you’re just passing an argument to a function, nothing needs to be dereferenced, since you have a pointer and the function is expecting a pointer.