@sveri: the behavior is a lot more transparent in 0.1.8-SNAPSHOT
, affected states would be restarted on namespace reloads: https://github.com/tolitius/mount/blob/0.1.8/README.md#recompiling-namespaces-with-running-states
@tolitius: Thanks for the quick update, I will definitly try it, if I find out how to include snapshot versions in my project. Anyone knows what I need to add to my leiningen definition?
Never mind, I had a typo
Ok, I opened two new issuse for everyone interested: https://github.com/tolitius/mount/issues/26 and https://github.com/tolitius/mount/issues/25
Also I'd like to thank you for your work, it's great to see an approach to components that is more clojuresque. I really love the idea :simple_smile:
@tolitus "Keep the code that interacts with external resources at the edges of the application. The core business logic should be pure and testable, while anything that deals with side effects and external resources should be pushed to a thin layer around it" This almost sounds like a hexagonal architecture used in a Clojure context. Is that the case?
that's how purely functional programs should be structured
which is conceptually similar to "hexagonal architecture"
@dm3 I'm now become slowly aware that paradigms I'm used to in the OO world have their origins from the functional (bow)
Hm, I have been thinking a lot about this. But, I am not so sure. Saying that anything dealing with side effects should be pushed to a thin layer around it, sounds like that's only a bit code. But doing any development that faces users or other clients I mostly have code that deals transforming data, from user to application to database or wherever and back. Mostly, the code that does any business logic is the smaller part of it
sveri: you may be able to express most of the data transformations in pure functions, and just have (read key)
and (write key val)
functions in your side effect layer
@sander: I know and mostly I do so, but still, the whole intent is to move data from a to b and adapt the to suit b's needs.
yeah being a thin layer it probably still won't be just a little bit of code
Currently I am playing stockfighter and for lvl 2 I built a whole web interface and websocket connections and a log and what not stuff. All interacting with their REST API or my web UI. It was a lot of code (not needed to solve lvl 2, but, well it was fun). And to solve lvl 2 I needed three functions integrated into all my other stuff. I guess that there like 2 % "business logic" compared to the other stuff connecting pieces.
But if you do this, then you have the benefit of being able to not touch the domain logic if you need to change the data store, or the user interface. I guess that's the main benefit of the hexagon.
I guess you could do that too if you abstract the data acess away into protocols or similar stuff. Indenependent from where they reside
So, I am not saying hexagon is bad or so. It's just the saying "put the side effecting stuff into a thin layer around the domain logic" suggests that the side effecting stuff is small and can be done in a few functions, but, from my experience the opposite is what usually happens. However, it's only my experience and surely depends on the domain.
I can imagine, if you do data analyisis where you have a connection providing a stream of data that you analyse and just pass on to another stream that there may be less glue code..
Well yeah, vanilla OO hexagon is heavily based around abstracting any outside communication into protocols/interfaces. I guess it might also depend on how you think about "side effecting stuff", but I agree the layer might not be as thin as one would hope, especially if the domain itself is simple. Writing a CRUD blog in a hexagonal way is probably way more effort than strictly required to just get it working. But when the ratio of the domain logic to the surrounding glue is more favourable (the domain is complex enough - maybe it's banking or shipping or commerce) then it would probably start coming out ahead in testability and maintenance compared to effort.
I think in general it's probably dependent quite a bit on the point of view - I've had a elective course on systems architecture this semester (first good course in six years...) and people had a hard time to understand the benefits Event Sourcing brings - if you have an immutable event stream and you can do time travel, speculative updates, easy merges and the application states basically lives only in-memory as a fold over the events and not like in more "normal" systems in an external database you really get a powerful toolset to deal with problems. It' s probably quite obvious for us here, since Clojure has parts of it built into the language with persistent data structures and reference types, Datomic uses some of those concepts in it's design and so on, but for students that have been learning programming for 3 years only and in imperative OO languages to boot? That was something they couldn't grasp at all.
And I'd hazard a guess it might be somewhat similar with those side effects you mention being pervasive. It might be just obvious to deal with them as you go, instead of pushing them to the outside due to how ingrained it is.
But it's a one big maybe - I just feel that sometimes things we think are necessary are not from another point of view. And maybe those side effects are not necessary where you want them, but are part of how you view the problem.
You could be right here. When I started on the web UI for stockfighter I was only doing side effecting stuff and building up state inside my application. So, I was basically only working in the "thin" layer and nothing else. When I was done with it I had only the thin layer, which is big, of course. Then adding the domain logic seemed like the easy task.
Could be.
I remember how hard Event Sourcing was to get for me, when everyone tried to explain it in terms of OO objects interacting with each other. I couldn't get it for the longest of times. And then I read somewhere that event sourcing is basically producing state by the way of left fold over a stream of events and I was enlightened. Similarly during this course when event sourcing was being explained one student was all like "but what if I had to fix a past mistake in event sourcing, don't I have to change past events then" and no matter how much he was being explained to he was all like "but if...", because to him mutability and the database being source of truth for the current state only was natural. If you come from the FP mindset the "don't change past, lay it over" is a natural approach.
So I guess at some point if you try to structure applications as to separate the domain logic from the external interface you will see how the lines are best drawn.
And I'm saying this to myself as well, I hardly know that either.
event sourcing ftw
@sveri: looks like a foo.bar.components.handler
from your example (great example btw :)) is missing a :stop
function (for both #25
and #26
). I added a bit more details in the github issues.
@tolitius: Thanks for looking at it. I already commented and will give it a "real" try later :simple_smile:
Are there transcripts of the cognicasts?
Question: How do I turn a number into a vector of its digits?
Is the number an integer or string representation of it? In the first case I would repeatedly do modular division by 10 to produce a list of digits, in the second exploding the string by characters and then turning them into numbers would be appropriate.
I think.
@jaen: It’s the first case
Yeah, then do modular division as long as you're non-zero and you should have a list of consecutive digits.
modular division? if someone has the patience, please explain. (new to clojure)
Is it using the modulus operator?
Yeah. It's usually %
if you used any C-like language.
In Clojure it's mod
.
Thanks
Wrapping my head around this pure(-ish) functional thinking
(seq (str n))
where n is your number
@jr: ok, why does the output look like (\1 \2 \3)? What are the backslashes?
It's a character
like '1'
in C-like languages.
(map int (str n))
will convert those to integers if that’s what you want
(defn digits [number]
(if (> number 10)
(conj (digits (quot number 10)) (mod number 10))
[number]))
Here's an example solution
For the integer case.
@jaen: wait, i’m getting an error after i pasted that in and then did (digits 123)
Interesting, I'm pretty sure I've pasted a working code from the REPL, maybe I messed something up when formatting.
boot.user=> (digits 12234729385)
[1 2 2 3 4 7 2 9 3 8 5]
What error do you get?
java.lang.IllegalStateException: Attempting to call unbound fn: #'sandbox16491/digits
That's weird, where are you compiling that?
Oh, maybe you can't define functions there
Try here - http://clojurescript.io/
Seems to work
tryclj worked fine for me
Hm, interesting, I've tried just now and it worked as well
Interesting.
Maybe I entered it wrong? 🤷
In any case, it worked on http://clojurescript.io
Heh, I tried changing the namespace to see if I could see the error for myself:
java.lang.SecurityException: You tripped the alarm! in-ns is bad!
Well, I guess I'm glad they thought of that. :simple_smile:
OK, something tells me that I probably need to get “The Joy of Clojure"
Because I don’t want to keep asking silly questions
@jaen, if you’re awesomely patient enough, could you please walk me through the logic of the function you wrote?
i don’t understand how it works for digits larger than 99
Well, if you're just starting out with Clojure I suggest getting "Programming Clojure" first (aka. The Swan Book). "The Joy of Clojure" is pretty great, but not as a first book.
As for logic.
It's pretty simple recursion.
If the number is less than 10
It's a single digit
So we just return it in a one-element-vector.
Otherwise we use conj
to add (mod number 10)
that is the number modulo 10 (which is the remainder of division, and remainder of division is equal to the last digit of a number)
to a result of recursive call to (digits (quot number 10))
where quot
is integer division - that is for (quot 4 3)
you get 1
.
So for example for (digit 123)
it will result in something like this if we do it step by step:
(digits 123)
(conj (digits 12) 3)
(conj (conj (digits 1) 2) 3)
(conj (conj [1] 2) 3)
(conj [1 2] 3)
[1 2 3]
@kopasetik: does that explain how it works well enough for you?
@kopasetik: If you’re looking for book recommendations, "Clojure for the Brave and True" was my first book clojure book, it’s very beginner friendly. I think the online edition is free. (http://www.braveclojure.com)
@jaen: Yes, that makes a lot more sense. Thank you :simple_smile:
No problem.
Anyway - for first book it's better if you go with my or tim_h's suggestion. Joy of Clojure is good only after you get somewhat comfortable with the language.
Does clojure have function hoisting?
@kopasetik: none that i’m aware of, but this might be close: https://clojuredocs.org/clojure.core/declare
Ok, I’ve read that clojure/lisp in general is so simple in terms of its set of native functions that you can fit such a set onto a napkin. Have you experienced that?
@kopasetik: Well, kind of. You do have a small set of building block functions/macros that take you a long way in Clojure. Having said that, core isn't exactly small... but a lot of that is more for convenience than anything else, I think.
@solicode Awesome response, thanks!
Is there a clojure/clojurescript style guide that I can consult to make my code as legible as possible?
@kopasetik: there’s a very very small set of functions that are sufficient to fully define Lisps, which may be what you’re thinking of. And then you build everything else in terms of those functions, building up to whatever level of abstraction you prefer. Clojure uses a richer set of base functions for convenience, and then gives you loads of other handy functions, but those are the ones built on top. You very definitely want an editor that handles the parens well so they don’t get confusing, and several of those have aggressive formatters you can use that’ll keep your code formatted properly at all times (or on save, if you like) so it doesn’t get too confusing. Dunno if anyone suggested this above, but you’ll probably want to join #C053AK3F9 — you can feel free to post loads of questions there, and there’s usually someone around to help out. Good luck & have fun!
@eggsyntax: Just joined #C053AK3F9. Thanks for all of the info!
If you're looking a for a good editor, then Curisve is pretty neat. Has structural editing, debugger, and other niceties.
OK :simple_smile: