notjack
2018-8-5 20:16:21

@markx structs are good and I recommend using them over both classes and lists for lots of things


markx
2018-8-5 20:19:43

@notjack I’m trying to understand the different between structs and classes without methods. Do you mutate structs?


notjack
2018-8-5 20:20:39

You can, but by default struct fields are immutable. I never use mutable fields personally


markx
2018-8-5 20:21:32

so, do you define functions that take a struct and other inputs, and then return a new struct?


notjack
2018-8-5 20:22:16

Yup. The specifics depend on whether I’m creating a value type or a behaviour type though


markx
2018-8-5 20:22:49

what’s a behaviour type?


notjack
2018-8-5 20:25:17

A type that’s meant to encapsulate some sort of function-like behavior, like (struct order (proc)) where proc is a procedure that compares two argument to see which is greater


notjack
2018-8-5 20:25:51

usually if you want to put a function in a struct or you want to make multiple instances of a class each with different implementations of a method, that means you’re making a behavior type


notjack
2018-8-5 20:26:18

Value types on the other hand are just plain data that are only manipulated by other, external functions


notjack
2018-8-5 20:26:40

a good rule of thumb for telling which you want is asking “should it be easy to serialize this?”


notjack
2018-8-5 20:27:17

also I don’t think “behavior type” is an official term anywhere, I think I just made that up


markx
2018-8-5 20:27:38

Hmm, how would you use this order struct, for example?


notjack
2018-8-5 20:28:31

first of all, I’d make sure the constructor is named make-order - I use make- as a prefix for constructors of behavior types (for reasons related to constructor-style printing and transparent-vs-opaque structs)


notjack
2018-8-5 20:30:34

then I’d make some functions like this:

;; returns one of '< '> or '=
(define (order-apply ord v1 v2) ((order-proc ord) v1 v2))

(define (order<? ord v1 v2) (equal? (order-apply ord v1 v2) '<))
(define (order>? ord v1 v2) (equal? (order-apply ord v1 v2) '>))
(define (order=? ord v1 v2) (equal? (order-apply ord v1 v2) '=))
(define (order<=? ord v1 v2) (not (order>? ord v1 v2)))
(define (order>=? ord v1 v2) (not (order<? ord v1 v2)))

markx
2018-8-5 20:37:08

So why do we embed the proc in a struct, instead of directly passing it around? Maybe it’s only useful when we need to group multiple functions together?


notjack
2018-8-5 20:43:48

a couple of reasons, all of which boil down to “because encapsulation”:

1) I get a predicate, order?, that I can use as a flat contract instead of typing (-> any/c any/c (or/c '< '> '=)) everywhere (which would add layer upon layer of wrapper functions since it’s a chaperone contract) 2) I get better error messages when I accidentally use an order where I meant to use some other function 3) I can optimize things related to orders because their representation is more limited, for instance there’s no need for order-apply to check the arity of proc when calling it 4) I have a central module to put all logic related to orders and I can enforce who can see the internal representation of orders by carefully choosing what that module provides to other modules


khepin
2018-8-5 20:44:57

When listening to filesystem changes with racket (filesystem-change-event "my/path") is there any way to get the path or any event information once the event is synced?


khepin
2018-8-5 20:45:11

I can’t find any doc around that


notjack
2018-8-5 20:46:45

@khepin do you mean some sort of function such that (func (filesystem-change-event "my/path")) returns "my/path"? because I don’t think that exists


notjack
2018-8-5 20:46:57

I can imagine the use for it though


khepin
2018-8-5 20:49:22

Yeah, what I was thinking was something like: (define (filter-accept-all x) #t) (define paths (find-files filter-accept-all)) (define evts (for/list ([f (in-list paths)]) (filesystem-change-evt f))) (define received-event (apply sync evts)) ;;; Below are the missing parts (println "Path: " (filesystem-event-path received-event)) (println "Event Type: " (filesystem-event-type received-event)) ;; eg: created, modified, deleted, etc...


markx
2018-8-5 20:56:38

@notjack So that’s mostly about the data type. Thanks!


greg
2018-8-5 21:01:52

@khepin It’s been awhile since I worked with it, but, I think filesystem-change-evt is a pretty thin wrapper over what the OS does. And the file change notification from the OS typically doesn’t give you that info. So you’ll need to do that yourself. In the case where you’re watching a file, you’ll need to record some information about the file, wait for the event, then check to see if the info changed. In the chase where you’re watching a directory, similar. You’ll want to record the current directory contents, wait for the event, then check the contents again to discover what changed.


khepin
2018-8-5 21:03:30

ok. It looks like I may have to go down to FFI wrappers anyway. Looks like the racket functions open one resource per file / directory. I think there are better options available for watching a large number of files (KQueue / FSEvents I believe)


greg
2018-8-5 21:06:23

The “only” value that filesystem-change-evt adds, is not needing to “poll”. One way to think about it, is, if it didn’t exist. (It didn’t exist in older Racket versions). Well you’d probably take a directory-list snapshot, start a thread, sleep, check directory-listing again, and if unchanged, loop and sleep again. With filesystem-change-evt, you don’t need the thread and sleep anymore, but you still need most of the rest of it.


khepin
2018-8-5 21:06:56

gotcha


greg
2018-8-5 21:07:12

If you know the OS, and know a better way, then FFI sounds like the way to go.


greg
2018-8-5 21:07:40

If you want to write portable Racket code, then filesystem-change-evt is better than what you’d have to do otherwise. :slightly_smiling_face:


markx
2018-8-5 21:08:26

@notjack @greg I found this article about Clojure. It says that I can save the states for a program in a global store, using things like var or atom and etc. Then most parts of a project would be pure functions, and the rest are about IO or mutating the states. http://lambdax.io/blog/posts/2016-04-18-state-management-in-clojure.html. So how, or what do I use, to do this in Racket?


greg
2018-8-5 21:12:40

Clojure ^:dynamic vars and its binding form are ~= Racket parameters (as in make-parameter) and its parameterize form.


markx
2018-8-5 21:13:51

how about atom? Is that something needed in racket?


greg
2018-8-5 21:13:59

Racket doesn’t have “atoms” and “refs” exactly but you could use a box for that and use things like a box-swap! function: https://github.com/greghendershott/rackjure/blob/master/rackjure/utils.rkt#L21-L27


greg
2018-8-5 21:15:19

More typically I think Racketeers would instead handle concurrency with say channels and synchronizable events. Racket has what I’m told is the whole Concurrent ML concurrency model.


markx
2018-8-5 21:17:38

Yeah that’s exactly what I want to know. What is Racket’s solution for state management? Is there something equivalent in Racket, or does Racket use another way to tackle this?


greg
2018-8-5 21:18:54

I think this is one of those areas where Clojure is “opinionated” and Racket is more like “well here’s a bunch of tools from computer science over the last few decades, use what you like”. :slightly_smiling_face:


greg
2018-8-5 21:19:22

It’s hard to discuss in the abstract, IMHO.


greg
2018-8-5 21:19:37

What kind of program are you writing? A web server app for instance?


greg
2018-8-5 21:19:47

I could talk through that.


greg
2018-8-5 21:20:51

Typically the “outer edge” of your program would do I/O, and most of your program would be a function from requests to responses.


notjack
2018-8-5 21:21:15

racket does not have any sort of STM (Software Transactional Memory (I think that’s the right acronym)) system and racket’s VM is single-threaded (you need places to use multiple cores and cross-place communication is limited) so you’d have to build a lot of infrastructure to do things the clojure way


notjack
2018-8-5 21:21:45

(note: “single-threaded” does not mean “not concurrent”)


greg
2018-8-5 21:22:17

I’m not an authority but I think STM was sort of an experiment in Clojure that hardly anyone actually uses these days? idk


notjack
2018-8-5 21:22:34

clojures refs are the STM system I think - or it’s atoms, or one of those things


notjack
2018-8-5 21:23:14

ah yes, refs are the STM system


notjack
2018-8-5 21:23:16

notjack
2018-8-5 21:24:08

as a rule of thumb, STM system = multiple boxes you can read+write in a transaction safely


markx
2018-8-5 21:24:27

ah that makes sense. So I can give up on that thought, keeping all states of a program in a global store.


notjack
2018-8-5 21:24:54

you can keep all state in a single box if you want and make the state value an immutable data structure


notjack
2018-8-5 21:25:12

that actually works a lot better than you’d think


greg
2018-8-5 21:27:01

I think Clojure atoms are pretty much just boxes with some @ reader syntax to unbox. My impression is real Clojure programs these days pretty much use atoms and not “refs” or “agents”. But I could very much be mistaken.


notjack
2018-8-5 21:27:54

that makes sense to me (also I’ve only written clojure once in my life and it was a tiny toy program)


markx
2018-8-5 21:28:12

hmm, but is it idiomatic to use box? Or is idiomatic not a thing in Racket?


greg
2018-8-5 21:29:00

(I’m definitely not saying Clojure is bad, I’m just saying the mechanics actually used are easy to do in Racket if you like that style.)


notjack
2018-8-5 21:29:33

looking over docs it looks like clojure atoms are pretty much equivalent to racket boxes


greg
2018-8-5 21:29:37

@markx It is not common to use box, in code I’ve seen.


notjack
2018-8-5 21:29:55

@markx I don’t know about the rest of the world, but I exclusively use box when I want mutation. But I rarely want mutation in the first place


notjack
2018-8-5 21:30:37

I also prefer to use immutable structs with mutable box fields when I need a struct with a mutable field (which, again, is not common)


markx
2018-8-5 21:31:37

Yeah that’s very good to know. I want to learn the common Racket way.


greg
2018-8-5 21:32:15

I think it’s hard to give advice because Racket programs vary widely in what they do, and of course what their authors prefer.


markx
2018-8-5 21:32:23

So how do you manage states? Or is states something to avoid in racket?


greg
2018-8-5 21:32:41

I think the general advice might be, try to keep mutation localized to one small “corner” of your program.


greg
2018-8-5 21:32:56

Try to make most of it be pure functions that take a value, and return a new value.


markx
2018-8-5 21:33:04

Yeah, it’ll be really nice if you have time to hear my use case. I actually have a lot of questions and I don’t know how to ask…


greg
2018-8-5 21:33:12

And your life will be easier, especially when debugging and dealing with concurrency.


notjack
2018-8-5 21:33:22

I would also say that yes, it is something to avoid, but you are not forced to avoid it or check that you use it correctly (like you are in Haskell)


greg
2018-8-5 21:34:17

@markx I’d love to hear about your use case. I do need to go make dinner in a few minutes. But I could check back later.


markx
2018-8-5 21:35:05

Yeah I think I know that I should make most parts pure, and keep the states in a corner. I just don’t know how… :sweat_smile:


markx
2018-8-5 21:35:59

Great! No rush at all. Thanks in advance!


greg
2018-8-5 21:36:02

Well in Racket it’s not necessarily horrible to have, say, (define the-state (make-hash)) and things bang on that mutable hash-table. I do see that.


greg
2018-8-5 21:37:19

A lot of programs keep state in some sort of key-value store. Whether that’s an external database, a mutable hash-table, or a set of global variables. :slightly_smiling_face:


greg
2018-8-5 21:37:42

Or the filesystem


greg
2018-8-5 21:38:18

Anyway I’ve got to run before @notjack barfs about what I just said :grin:


markx
2018-8-5 21:38:30

hahaha go ahead.


markx
2018-8-5 21:38:37

Are you familiar with MUD?


markx
2018-8-5 21:38:42

or mud client.


notjack
2018-8-5 21:38:53

I’ve not lost my lunch yet @greg ;)


markx
2018-8-5 21:39:28

If not, I guess I need to draw some diagrams. I’m trying make a simple mud client.


notjack
2018-8-5 21:40:14

@markx I’m only vaguely familiar with the notion of MUDs - they’re kinda like text-based rpgs but with a massively multiplayer world right? or am I thinking of something else


markx
2018-8-5 21:44:37

Right, exactly.


markx
2018-8-5 21:47:57

I’ll need sometime to think about how to describe the project…


zenspider
2018-8-6 01:44:30

@markx you might want to look at how big-bang does state. Implement a small game in it (or visualize your MUD—but I’m really just suggesting you use it to experiment with ideas)