samth
2022-2-25 15:41:02

I also think many of the ideas you described for Go are also applicable — structs, goroutines (basically threads in racket), channels.


thechairman
2022-2-25 15:47:35

it might be heavier than you’re hoping for, but Software Design for Flexibility by Hanson and Sussman is pretty good as an intro to typical FP design approaches, written in Scheme


bsilverstrim
2022-2-25 18:45:35

Thanks, I’ll take a look at the book! Also, @samth - racket has channels for communication?



sschwarzer
2022-2-25 19:46:47

> I just got a copy of Racket Programming the Fun Way, so I’m hoping that will shed some light on things as I’m working through it. If I don’t confuse something, someone mentioned recently in an online meetup that “Racket the Fun Way” often was using a rather “mutating style”, which would be rather untypical for Racket and Scheme.

@all Let me know if I confuse something. Was this possibly about another book?


sschwarzer
2022-2-25 20:06:26

@bsilverstrim A few things I can think of: • My modules typically define one or more structs and functions that operate on the struct. Modules are my primary “encapsulation device.” • It’s typical not to mutate instances of structs, but rather return modified structs, see https://docs.racket-lang.org/reference/struct-copy.html#%28form._%28%28lib._racket%2Fprivate%2Fbase..rkt%29._struct-copy%29%29 . • The same goes for lists. By far the most list functions return new lists. • If I use mutation, I try to keep it local to a function, so the function looks like a pure function (i.e. without side effects) from the outside. • Where my program does I/O, I try to write most of my code in pure functions that are then easier to test. I put the I/O in other functions that call the pure functions. • My programs typically are functions calling other functions, very rarely mutating state. You can define functions on the module top-level, but especially for small helper functions that you use only in a singe function, it’s common to put them in the function where they’re needed.


thechairman
2022-2-25 20:08:33

yeah, in general maintaining a strict boundary between the well-behaved functional core and sources of mutation or side effects is a typical program structure


sschwarzer
2022-2-25 20:08:58

Note that conditionals in Racket/Scheme (if, cond, case etc.) behave like functions, so you can use them as such. Think of the language only having if expressions, but no if statements (and correspondingly for other conditionals).


thechairman
2022-2-25 20:09:30

either by encapsulating them in something that behaves functionally on the outside or pushing them to the outer boundary


sschwarzer
2022-2-25 20:09:40

@thechairman Yes, much of what I wrote would make sense for programs in imperative languages, too.


bsilverstrim
2022-2-25 20:26:23

Sounds like part of the philosophy is to not take a struct of data and alter information in it, but instead pass a copy of a struct, mutate-alter-change what is needed to be operated on, and return a new version back to a caller.


bsilverstrim
2022-2-25 20:27:20

less playing with pass-by-reference, more pass-by-value.


sschwarzer
2022-2-25 20:32:00

Most Racket APIs don’t even alter data. You typically don’t make copies explicitly (not even in your own functions). Take for example cons ( https://docs.racket-lang.org/reference/pairs.html#%28def._%28%28quote._~23~25kernel%29._cons%29%29 ). You pass in two values and get out another. The original values aren’t changed: (define my-list '(1 2 3)) (define my-list-with-an-item (cons 4 my-list)) my-list -> '(1 2 3) my-list-with-an-item -> '(4 1 2 3) Lists are implemented as singly-linked lists that share data if possible. For example, cons doesn’t make a copy of my-list, but creates a new node that contains the new value 4 and points to my-list for the rest of the list.


samdphillips
2022-2-25 20:35:01

That said there are some APIs (in the standard library and elsewhere) that do mutate data. But I encourage trying not to mutate at first.


sschwarzer
2022-2-25 20:35:04

The good thing is that if your data is immutable, you can pass it by reference internally (for performance) and still don’t have to worry about modifying it.


sschwarzer
2022-2-25 20:37:07

@samdphillips Exactly. :slightly_smiling_face:

@bsilverstrim For example, there are immutable and mutable hashes: https://docs.racket-lang.org/reference/hashtables.html