ryanc
2021-3-4 09:20:28

There’s a way of handling letrec with right-hand sides restricted to lambda expressions without mutation or cyclic data structures. You can change your environment representation to have two kinds of frames: normal and recursive. A normal frame maps variable names to values; a recursive frame maps variable names to lambda expressions. If you look up a name and find it in a normal frame, you return the value. If you look up a name and find it in a recursive frame, you create and return a closure of the lambda expression and the environment starting with recursive frame where you found the name.


samth
2021-3-4 14:22:17

this does feel like a situation in which nix etc is trying to treat everything like a C program even in cases where that doesn’t make sense


ben.knoble
2021-3-4 16:59:12

Well, I managed to get it working by having define only available at the top-level, and pulling it’s evaluation farther up the eval tree than normal. No recursion (we don’t have even conditionals), so all the stuff about closures and stuff is sort of moot.

In spite of that, the following program works in my implementation (yay Church!) define Z lambda (f) let (A lambda (x) (f lambda (v) ((x x) v))) (A A) define czero lambda (f) lambda (x) x define cone lambda (f) lambda (x) (f x) define csucc lambda (n) lambda (f) lambda (x) (f ((n f) x)) define cplus lambda (m) lambda (n) lambda (f) lambda (x) ((m f) ((n f) x)) define cpred lambda (n) lambda (f) lambda (x) (((n lambda (g) lambda (h) (h (g f))) lambda (u) x) lambda (u) u) define cmult lambda (m) lambda (n) lambda (f) (m (n f)) define c-to-nat lambda (n) ((n lambda (n) (+ n 1)) 0) define ctrue lambda (a) lambda (b) a define cfalse lambda (a) lambda (b) b define c-to-bool lambda (b) ((b (= 1 1)) (= 0 1)) define czero? lambda (n) ((n lambda (x) cfalse) ctrue) define cif lambda (p) lambda (a) lambda (b) ((p a) b) define !-prime lambda (f) lambda (n) ((((cif (czero? n)) lambda (x) cone) lambda (x) ((cmult n) (f (cpred n)))) ;; this last just forces the thunk returned by cif ;; eta-expansion required because otherwise all arguments are fully ;; evaluated czero) define ! (Z !-prime) define c7 (csucc (csucc (csucc (csucc (csucc (csucc cone)))))) (c-to-nat (! c7))


sschwarzer
2021-3-4 18:35:52

What is more idiomatic/recommended? (map (lambda (task) (task-field task 'completed?)) tasks) or (for/list ([task tasks]) (task-field task 'completed?)) ?


sschwarzer
2021-3-4 18:39:12

I would use map if I already have the function to apply. I would use for/list if the body is a more complex expression. In this case neither of these applies. :slightly_smiling_face:


sschwarzer
2021-3-4 18:51:29

Another question: I want an expression that returns #t for any “truthy” value and #f otherwise. I can do that with (not (not v)). Is there a more straightforward way?


dyllongagnier
2021-3-4 18:55:24

I almost always end up using for/* for whatever I’m doing. I personally think it is more readable and it leaves more room for extension.


ben.knoble
2021-3-4 18:59:05

I can think of (if v #t #f) or (and v #t) ; only the first is immediately clear without additional reasoning.


sschwarzer
2021-3-4 19:02:22

@dyllongagnier Would you also use for/fold in the above case if the map where filter? Or is there another for idiom for that?


samth
2021-3-4 19:02:33

(and v #t) is the more-usual way in Racket


samth
2021-3-4 19:02:55

@sschwarzer you can use #:when in the for/list to include a filter


sschwarzer
2021-3-4 19:03:07

Ok, so (and v #t) is actually idiomatic?


samth
2021-3-4 19:03:19

yes


ben.knoble
2021-3-4 19:03:29

Hm, just thinking about it, wouldn’t it be natural for (and v #t) to expand to (if v #t #f)?


samth
2021-3-4 19:03:43

it does expand to that


sschwarzer
2021-3-4 19:04:27

@samth Right, nice. :slightly_smiling_face: The for forms have so many options that I sometimes miss the forest for the trees.


sschwarzer
2021-3-4 19:05:47

I hope I never have to adapt my Racket code to a Scheme. :satisfied:


sschwarzer
2021-3-4 19:21:42

Do you have recommendations for well-written Racket code I could read to get a better understanding of design approaches and idioms? The background of my question is that I’m rather new to Racket and to Lisp/Scheme and sometimes I wonder if I’m doing things in unneccesarily complicated ways.


spdegabrielle
2021-3-4 19:24:03

The style guide has some examples I know it’s not quite what you are asking for but it helps me https://docs.racket-lang.org/style/Units_of_Code.html\|https://docs.racket-lang.org/style/Units_of_Code.html


sschwarzer
2021-3-4 19:28:54

Yes, I’ve read the style guide and found it useful. :slightly_smiling_face: I guess I’m now looking for a higher-level design help. Maybe more like patterns than idioms (although I expect “patterns” in Racket to be much more simple than in languages like Java).


sschwarzer
2021-3-4 19:35:00

I remember I had started with “How to design programs” a while ago, but lost interest because • It seems targeted at programming beginners and I found it progressed too slowly. • I didn’t want to learn the beginner languages and then “unlearn” them and after that learn Racket. I wanted to learn Racket directly.


salkvus
2021-3-4 19:35:28

@salkvus has joined the channel


sschwarzer
2021-3-4 19:39:13

(I do have a lot of experience in imperative programming, especially OOP. So I’m looking for FP/Lisp/Scheme/Racket-specific design advice, not so much for “paradigm-independent” advice.)


greg
2021-3-4 20:43:59

It sounds like I was similar to you when I first learned Racket. I think, for me, “all the parentheses” was not so big a deal. Breaking my habit of mutating objects was harder, at first. Especially on a “local” or “tactical” level. Just things like, “well I need to loop” and having to stop myself from using set! and instead using map or for/xxx or whatever. But I see you’re already focusing on those, so great! I think from there it gets easier thinking in terms of composing functions — “chains” of functions that accept and return values. Instead of functions that poke some data structure. You may just need some time to rewire your brain, how you approach things.


greg
2021-3-4 20:46:11

Another “idiom” you might find useful is pattern matching. Pretty frequently you’ll have a function that returns a value. You need to test the value, e.g. do different things if it’s false or some other value. And you also want to bind the value to a variable, to use further. Using match for that is useful. You may find the doc page a little daunting at first — there are so many options! — but with a little time it will make sense and feel comfortable.


greg
2021-3-4 20:47:21

(You can also just bind a value to variable with define or let, and then use if or cond as a separate step. But doing it in one swell foop can make certain code cleaner. In most people’s opinion, AFAIK.)


laurent.orseau
2021-3-4 21:48:07

Well, if there’s a repeating pattern, that’s often replaced with a macro :slightly_smiling_face: That’s why lisp/scheme/racket code is compact, since it can push DRY pretty far.


laurent.orseau
2021-3-4 21:52:35

So i guess my advice would be to learn macros when you feel ready


sschwarzer
2021-3-4 22:11:51

Many, many thanks for all your replies to my code/design questions! :heart: I’ll give some answers in the threads.


sschwarzer
2021-3-4 22:18:03

I read the chapter on macros in the Racket Guide and I read Greg’s (great!) tutorial on macros. I’ll look into using macros when I think it might be appropriate. :slightly_smiling_face: Maybe I’m not ready yet to see when a macro would be the best solution. It’s a hint if the code is repetitive, but if my problem isn’t repetition or if I don’t see the repeating pattern, macros won’t help. :wink:


sschwarzer
2021-3-4 22:20:33

Fortunately, I got (somewhat) used to the functional style a few years ago when I looked into Haskell for a bit. This stumped me at first. With my over 30 years of programming experience, I felt like a programming beginner. :satisfied: Since I looked into Haskell, I wanted to do more with FP, but it kinda fell by the wayside for several years.


sschwarzer
2021-3-4 22:22:09

Regarding pattern matching: I used it in Haskell and like it. In Racket I kind of rather avoided it so far because at the moment I write smallish command line programs and when I require racket/match this about doubles the startup time. (I wrote about this further up in the channel.)


sschwarzer
2021-3-4 22:23:27

By the way, many thanks for your macro tutorial https://www.greghendershott.com/fear-of-macros/ . :+1: I read it recently (but haven’t written any macros yet). I worked a bit with macros in Nim, though.


sschwarzer
2021-3-4 22:26:03

Regarding define and let : Yes, I use them sometimes as “pseudo-imperative” code. I can see the order of “steps” more easily. I follow the Racket style guide and use rather define than let if possible.


sschwarzer
2021-3-4 22:31:13

Here’s an example of what I was/am struggling with today. I didn’t post it earlier because I was still working on it: https://git.sr.ht/~sschwarzer/todoreport-racket/tree/364b75bd493a8544a3e2a5b55098e4fda21a7bb9/item/task.rkt#L325 To abstract the handling for different kinds of task fields (see https://github.com/todotxt/todo.txt#todotxt-format-rules ), I created a function which creates two functions depending on the field type. (I’d say this is conceptually similar to OOP where I would maybe use three classes with two methods each.) My approach seems to work ok, but I wonder if that’s the way to go since the function grouping-funcs is deeply nested. Then, I have the impression you can get away with deeper nesting in functional code than in imperative code. :slightly_smiling_face:


gknauth
2021-3-4 22:33:40

When I went to @mflatt’s class on HtDP for teachers back in 2007, even though I’d known about DrScheme for years already, and Scheme for more years, and Lisp since about 40 years ago (early college), I’d never been presented any style information, it was mostly “do what other people do, see if you can improve on it.” When I saw the HtDP patterns, my immediate reaction was “why didn’t anyone show me this in 1980!” I guess because it didn’t exist. But so many things people do all the time are covered by HtDP patterns. I get what you say about it being oriented to beginners, at least the early material. I think it’s kind of perfect for college students new to programming. It just didn’t exist when I was in college, and I already knew BASIC Assembly Forth Fortran APL and I’d even implemented a few languages of my own even though I had no idea what I was doing. In a way, that one week class in 2007 was perfect for me to “get it.” Those “Teach Scheme!” classes aren’t run any more, I don’t think, but they were great. I suppose these days you could make a series of YouTube videos that ran experienced programmers through the fundamentals of HtDP.


sschwarzer
2021-3-4 22:33:41

Overall, I find FP very intriguing. :slightly_smiling_face:


sschwarzer
2021-3-4 22:35:50

Many thanks. So I guess I’ll have another look at HtDP. :slightly_smiling_face:


gknauth
2021-3-4 22:45:07

That project looks pretty interesting. I wonder what the Emacs org-mode people would think of it.


sschwarzer
2021-3-4 22:46:13

The todo.txt on Github isn’t my project. I’m merely writing a Racket program (the Sourcehut link) to process these todo.txt format files. :slightly_smiling_face:


sschwarzer
2021-3-4 22:47:28

Ah, you said that project, not your project.


sschwarzer
2021-3-4 22:47:44

I wasn’t sure if we had a misunderstanding.


gknauth
2021-3-4 22:48:45

I looked at your code. If you’re new to Racket, I think you’re off to a great start. It is very readable. You have comments, OMG. Structurally what you’re doing makes sense to me. I guess the function you think is very nested doesn’t look that nested to me. The number of things you’re doing is finite and small. It’s not like you’re juggling two dozen things. Maybe 3 balls, not 50. So the nesting doesn’t bother me at all. I see bunches of lambdas and I think “that’s a little unusual” but there’s nothing at all wrong with it. I guess in other people’s code I might expect to see those things broken out into small functions of their own that you could call from the bigger function. But if you’re only doing those things in that one place, having those very local functions is fine (to me). I can see what’s going on.


capfredf
2021-3-4 22:51:09

If I really wanted to get rid of the local lambdas, I would turn those closures into top-level functions.


sschwarzer
2021-3-4 22:51:15

@gknauth Wow, thanks for the great feedback. I’m somewhat relieved that you say I’m on a good path.


gknauth
2021-3-4 22:51:33

By the way, Jerry Sussman told me his newest book is coming out next week so I ordered it yesterday. I don’t know if it’s appropriate to your questions here but from the title and the description I read and what he told me, it might be, once you really want to feel great about your code. https://www.amazon.com/Software-Design-Flexibility-Programming-Yourself/dp/0262045494/


capfredf
2021-3-4 22:51:34

And then use a hash table to dispatch functions


gknauth
2021-3-4 22:52:04

I’ve seen some ugly code in my life and your code is anything but. No, you’re doing great.


sschwarzer
2021-3-4 22:52:32

Regarding the comments, that’s something that’s important to me. I actually gave a talk on that subject in 2019: https://sschwarzer.com/download/comments_pycon_de2019.pdf


capfredf
2021-3-4 22:54:16

But your code is absolutely comprehensible to me. I wouldn’t worry about the extensibility for the time being


gknauth
2021-3-4 22:57:29

The only problem I have with comments is when they are not maintained, they are next to useless. At work I sometimes have to look at Java comments that were written, beautifully, in 2005 or 2006, but the comments have very little to do with what the code does the last time someone touched it. So by habit I look at code first to see what’s happening, and I only look at comments if I can’t make sense of the code.


badkins
2021-3-4 22:59:11

I’m looking forward to that one. I may wait for a 2nd printing to handle some errata since I’m way behind on reading anyway :)


sschwarzer
2021-3-4 22:59:37

I try my best to keep comments up to date. :slightly_smiling_face: My approach is that still comments can get out of date, but the advantages outweigh the disadvantages. (If the comments are usually written well.)


sschwarzer
2021-3-4 23:20:34

By the way, your mention of Forth gives me a nice nostalgic feeling. :slightly_smiling_face: I came across Forth in the 1980s and liked it a lot. A very simple, yet very flexible language (like Scheme).

I remember I wrote a DSL in Forth for horizontal and vertical text UI menus.


sschwarzer
2021-3-4 23:33:43

Thank you! :slightly_smiling_face:


gknauth
2021-3-4 23:57:15

I was lucky to attend Chuck Moore’s talk in person: https://www.infoq.com/presentations/power-144-chip/


sschwarzer
2021-3-4 23:58:41

Cool :slightly_smiling_face:


sschwarzer
2021-3-4 23:59:38

By the way, I think “Thinking Forth” was the first book on software design that I read.


kellysmith12.21
2021-3-5 03:31:28

Do Racket’s universal and existential contracts have the same expressive power as the universal and existential types from the static typing world?


samth
2021-3-5 03:52:56

There’s substantial literature on this; I think the shortest answer is that it’s complicated


samth
2021-3-5 03:53:22

See, for example, Blame for All, POPL 2011 and all the papers that cite it subsequently


samth
2021-3-5 03:56:47

Note that that isn’t about exactly what Racket does, and comparing types to contracts is far from precise anyway


samth
2021-3-5 03:57:23

See also Arjun Guha’s 2008 paper which is about what Racket does, but is less formal


wanpeebaw
2021-3-5 05:39:00

I’m recently learning Forth. It’s one of those few languages ​​with powerful macro ability like Lisp/Scheme.

It’s very fun but also very obfuscating, LOL!


kellysmith12.21
2021-3-5 06:14:38

Thanks for the info!


wanpeebaw
2021-3-5 07:05:50