@markx structs are good and I recommend using them over both classes and lists for lots of things
@notjack I’m trying to understand the different between structs and classes without methods. Do you mutate structs?
You can, but by default struct fields are immutable. I never use mutable fields personally
so, do you define functions that take a struct and other inputs, and then return a new struct?
Yup. The specifics depend on whether I’m creating a value type or a behaviour type though
what’s a behaviour type?
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
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
Value types on the other hand are just plain data that are only manipulated by other, external functions
a good rule of thumb for telling which you want is asking “should it be easy to serialize this?”
also I don’t think “behavior type” is an official term anywhere, I think I just made that up
Hmm, how would you use this order
struct, for example?
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)
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)))
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?
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
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 sync
ed?
I can’t find any doc around that
@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
I can imagine the use for it though
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...
@notjack So that’s mostly about the data type. Thanks!
@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.
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)
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.
gotcha
If you know the OS, and know a better way, then FFI sounds like the way to go.
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:
@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?
Clojure ^:dynamic
vars and its binding
form are ~= Racket parameters (as in make-parameter
) and its parameterize
form.
how about atom
? Is that something needed in racket?
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
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.
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?
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:
It’s hard to discuss in the abstract, IMHO.
What kind of program are you writing? A web server app for instance?
I could talk through that.
Typically the “outer edge” of your program would do I/O, and most of your program would be a function from requests to responses.
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
(note: “single-threaded” does not mean “not concurrent”)
I’m not an authority but I think STM was sort of an experiment in Clojure that hardly anyone actually uses these days? idk
clojures refs are the STM system I think - or it’s atoms, or one of those things
ah yes, refs are the STM system
as a rule of thumb, STM system = multiple boxes you can read+write in a transaction safely
ah that makes sense. So I can give up on that thought, keeping all states of a program in a global store.
you can keep all state in a single box
if you want and make the state value an immutable data structure
that actually works a lot better than you’d think
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.
that makes sense to me (also I’ve only written clojure once in my life and it was a tiny toy program)
hmm, but is it idiomatic to use box
? Or is idiomatic not a thing in Racket?
(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.)
looking over docs it looks like clojure atoms are pretty much equivalent to racket boxes
@markx It is not common to use box
, in code I’ve seen.
@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
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)
Yeah that’s very good to know. I want to learn the common Racket way.
I think it’s hard to give advice because Racket programs vary widely in what they do, and of course what their authors prefer.
So how do you manage states? Or is states something to avoid in racket?
I think the general advice might be, try to keep mutation localized to one small “corner” of your program.
Try to make most of it be pure functions that take a value, and return a new value.
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…
And your life will be easier, especially when debugging and dealing with concurrency.
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)
@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.
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:
Great! No rush at all. Thanks in advance!
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.
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:
Or the filesystem
Anyway I’ve got to run before @notjack barfs about what I just said :grin:
hahaha go ahead.
Are you familiar with MUD?
or mud client.
I’ve not lost my lunch yet @greg ;)
If not, I guess I need to draw some diagrams. I’m trying make a simple mud client.
@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
Right, exactly.
I’ll need sometime to think about how to describe the project…
@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)