What’s happening here? I’m trying to install the REBL and I get this:
cognitect-dev-tools-0.9.58 % bash ./install
Installing: com.cognitect/rebl {:mvn/version "0.9.242"}
./install: line 5: mvn: command not found
Installing: com.datomic/dev-local {:mvn/version "0.9.232"}
./install: line 7: mvn: command not found
Looks like you need to install Maven (mvn)
Thank you, I ran brew install maven
and it worked 🙌️
i was referring clojure book and I come across (def app (middleware/wrap-base #'app-routes))
, what does #
` do here?
here app-routes
are list of routes
why we cannot use as (def app (middleware/wrap-base app-routes))
You can but without the var quote, if you change app-routes
while you app is running in the REPL, that change will not be seen.
With the var quote, there’s an extra level of indirection, so changes made by the REPL take effect without a restart.
See https://clojure.org/guides/repl/enhancing_your_repl_workflow#writing-repl-friendly-programs (that whole guide is worth reading).
@seancorfield, I got that, but that wont be helpful while running code in production right? because production code will be freezed
is there an easier way to apply a list of functions (with side-effects) to a map? the way I came up with: (reduce (fn [state f] (f state)) initial-state function-list)
btw, I reverted back to the reduce function from the beginning, because I got stack overflows when there were too many functions
You want the behavior in dev.
Thanks @seancorfield
But you can also have that behavior in production if you want. We run REPLs in some of our production processes and modify them live, while they are running.
How about
(doall ((apply comp (reverse function-list)) initial-state))
?
edit: added missing parens
I don't have a repl in front of me right now but I think that would also work
thanks
didn't think about comp, it does make it easier
is the doall necessary? the function-list is lazy, but applying comp returns a function that shouldn't be lazy
also, since my function-list is symmetrical, I'll leave out the reverse - leaving a short and nice ((apply comp function-list) initial-state)
- thanks for your help!
> applying comp returns a function that shouldn't be lazy Not so sure about this to be honest :thinking_face:
Ah, it seems you're right!
(->> (range 12)
(map (apply comp
[#(do
(println "Realized" %)
(- %))
#(+ 1 %)
#(* 2 %)]))
first)
evaluates to -1
but prints:
Realized 1
Realized 3
Realized 5
Realized 7
Realized 9
Realized 11
Realized 13
Realized 15
Realized 17
Realized 19
Realized 21
Realized 23
how to convert all the values from an map to string in clojure i have map like {:1 one :2 two}
to {:1 "one" :2 "two"}`
strait forward solution is to use reduce-kv
(reduce-kv
(fn [acc k v]
(assoc acc k (str v)))
{}
{:x 1 :y 2})
but it wouldn’t help if you have a map with nested maps. if that is the case you could use recursionyeah that works!!!any inbuilt function available in json ? I got :`key-fn` but did not get how it is used
depends on underlying library. I think jsonista has some functionality to add custom encoder/decoder
Documenting some of my early experiences learning Clojure. A caveat is that this is my first general purpose language that I’m really getting into (I tried teaching myself Python a few years back but got bored with that, probably because I didn’t have much of a goal at the time) https://twitter.com/RobertHaisfield/status/1402249772543614976?s=20
(into {} (map #(vector (first %) (str (second %))) {:1 :one :2 :two}))
Does anyone know any good animations that show how `apply` works in Clojure? Something like these? https://jstutorial.medium.com/map-filter-and-reduce-animated-7fe391a35a47
You asked this yesterday and got some great responses from really knowledgeable people. Not sure what else there is to say if you don't have a specific question about apply
Forgive me if this question is beyond me as I am not very versed in clojure but, do you mean like behind the scenes/how its implemented or something? Conceptually I think apply might be easier to understand by just remembering that:
; this:
(apply + (list 1 2 3 4 5))
; is the same as this:
(+ 1 2 3 4 5)
this doesn't exist in most other languages because they dont have s-expressions but I guess this kind of looks like it does the same as reduce until you understand that it it isnt (see above). the way I think of it is that it is a convenience function that exists because some times its nice to have. like the identity function. doesnt make much sense as to why it exists (to me at least) until you need it.
@dpsutton Oh wow I totally missed those. I’m used to Slack channels where people respond in threads vs. just in the channel, my bad
I’m gonna look back over them
So is apply basically bursting the arguments out of the list they were in?
The concept of apply
is clearer to me now, so I guess I’m closer to figuring out how I would visualize it… still can’t find any examples of it animated
it just takes the function given as the first argument and "applies" it to the list given as the second argument.
in lisp terms you just stick the first argument at the front of the second argument which is a list, easy as
user=> (apply + '(1 2 3 4 5))
15
user=> (+ 1 2 3 4 5)
15
I think it might be confusing because:
a) it looks like it does the same thing as reduce:
user=> (reduce + '(1 2 3 4 5))
15
b) it doesn't exist without s-expressions
but its really simple (at least conceptually), dont overthink it 🙂comp returns a lazy function when the first function arg is lazy
whether you apply it to a lazy arg is irrelevant as it eagerly consumes its args
@pavlos your example from map isn't showing what you think it is - map
is lazy (even here) but laziness across some data types (like vectors and ranges) is chunked - chunking means that realizing one item means realizing n items (where n is usually 32)
Ah, I knew about chunking but I thought n was something like 8. Didn't think to test with a larger input, thanks 🙂
funny thing, when I run that code but change (range 12)
to (range)
it only realizes the one item, I wonder what trickery is going on there
perhaps (range)
doesn't have the chunking behavior that (range n)
does
if I give it (range 1000)
it stops at 63 which is the 32nd result
so yeah, chunked by 32
but don't count on that number in your code - just account for the fact chunking of some N is possible
anyway none of this is related in any way to comp
my version: (into {} (map (juxt (comp identity key) (comp str val))) m)
almost like @yairt’s but notice that map
is being called with only one arg, and I use key
/ val
instead of first
/ second
how would I animate the application of a function to args? it's not multi step, it's a single event
thinking it through....
(apply + 1 2 [3 4 5])
becomes
(apply + [1 2 3 4 5])
which finally becomes
(+ 1 2 3 4 5)
I don't think there's more than that you could demonstrate - would an animation with those three steps actually help?
@rob370 Any documentation for apply.. I am trying to understand it ..
did not get when compare to reduce and map
it's not in the same category with those at all
it takes a function and args and calls the function
maybe my three step example above helps? maybe it doesn't. note that the simplification I show is conceptual and not reflecting the actual code - it's very low level clojure code that implements it
@popeyepwr perhaps the source of your confusion is that for associative functions like +
and *
and conj
using apply
and using reduce
happen to return the same result? that's not something special about apply, that's something special about those functions and how they treat their args
Perhaps an animation could show the apply and the brackets around [3 4 5] fading away
interesting - I guess I internalized this so long ago I lost my intuition for what is counter-intuitive when first learning it 😄
I guess that middle step I provided is arbitrary, and arguably should be skipped, I wanted to highlight the vararg behaviour of apply because I've seen too many experienced clojure users do things like (apply f (concat [a b] other-args))
where (apply f a b other-args)
already works
but that's more intermediate to journeyman level rather than beginner level
Yeah broadly speaking I’m really interested in tooling around “learnable programming” to help people understand what’s actually happening. From what I can tell, once people pick up the mental models they don’t struggle to visualize things in their heads but things like Reduce and Apply are pretty foreign to me (a brand new programmer)
One possibility I’m exploring is a more interactive REPL that could visualize things like this so people can have a visceral sense for how a function executes
data.json is soemthing totally different - it turns the whole hashmap into a string that javascript can parse
I’ve worked on the onboarding experience for GuidedTrack (a domain-specific language meant for non-programmer researchers and educators) and it has made me more confident in the idea of domain-specific languages as a vehicle for end-user programming and maybe even an alternative to traditional apps. Clojure is my first general purpose language I’m learning! https://www.youtube.com/watch?v=ov3lynWp9oM&t=13s
that's cool. one thing to note is that clojure is intentionally "a language for experts" and in any design conflict between intuition for beginners and flexibility / optimal behavior for experts, the experts will win. there's at least one youtube talk where Rich describes this in length, I'm looking it up now
I've even told people in the past clojure is a bad first language (don't let this stop you if you enjoy clojure of course)
Yeah, not sure what I was thinking there with map.
So whether this is lazy or not: ((apply comp (reverse function-list)) initial-state)
depends solely on whether the last function in the function-list is eager or lazy, right?
transcript here: https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/DesignCompositionPerformance.md talk here: https://www.youtube.com/watch?v=MCZ3YgeEUPg
right, reverse makes it more complex, but yeah, comp is never lazy, the laziness of the function comp returns is determined by the first function in its arg list
in general, laziness in clojure is a behavior of a data type, and a function is lazy if it returns one of those lazy types
and comp's return type is the return type of its first arg (this shouldn't be hard to figure out by first principles)
Yeah that’s fair that it’s designed as a language for experts. I think in general I’m not interested in modifying the language so much as just making more intuitive and obvious tooling around it and I think that Clojure’s generally uniform syntax opens up a lot of possibilities. Racket is another target for the sort of stuff I’m working on. Macros may ruin all of my plans lol
When I first started playing with Clojure, its generally uniform syntax made it way less intimidating for me than Python was when I attempted learning that a few years back
if you get overwhelmed by the special cases and corner cases of clojure, scheme shares the simplified syntax and is also explicitly made for beginners / education. the racket
version in particular has the best docs I've ever seen for any programming language
but unlike clojure it doesn't have the conveniences for enterprise software, so it hasn't taken off in the industry
but so far it doesn't sound like you are at all interested in enterprise software
not telling you you are in the wrong place, but rather mentioning the kind of thing that comes up in my experience for people coming to clojure the way you are
racket definitely has the sorts of visualization of program steps you are talking about here in its IDE
I think sometimes apply and reduce will give the same result
(and, big picture, apply and reduce and map work the same way in clojure and in racket)
right, but that's a property of the function itself not a property of reduce / apply
Haha yeah I’m not personally interested in a career in enterprise software. I’m interested in replacing basic app functionalities with higher order functions so people can do the sort of stuff they were already interested in doing but with control flow and recursion to give them more flexibility over what a traditional GUI app would
yeah, racket is meant for that kind of thing
but please do continue using clojure, it's a great language 😄
And then providing tooling around those DSLs so people ramp up in skill level towards just writing fluently
That clears it up quite a bit, thanks.
Yeah Racket is definitely more purpose built for the sort of thing I’m interested in, it just seems more niche. I’ve made a basic program in it, and I just need to learn the different sorts of data structures
I guess I don't really grok the difference yet
oh yeah, that's some historical baggage from the lisp family (it's simpler to just use lists for everything right???)
One dilemma was basically: Clojure is built with smart defaults that will help me form a basic mental model. Racket has an encouraged style but is more agnostic. So I wasn’t sure if I should start out by forming my default mental models around Clojure mental models or by forming my own models from working with Racket
but I will do some more reading ^.^ probably best just to see lots of examples
there are libraries that add data types to racket but most racket examples and code are going to be lists, lists and more lists
that's an interesting perspective, I definitely "leveled up" in software design by understanding clojure's design decisions
Yeah. Clojure does a lot of clever things to infer parentheses over pairs of things where Racket would make you manually specify them. I like how in Clojure the [ ] and { } indicate different types of things than the lists, but ultimately mean the same thing as (vector ) and (hashmap ). Makes it easier to visually parse.
Okay apply is variadic and takes potentially many args Reduce must take a function of two inputs... and then args is that right?
another thing to look into (also another input to clojure's design) is the ml family (OCaml being the biggest living example) which tends to create too many data types instead of having too few
the variadic part is a distraction for your question
reduce walks one function across many items
apply calls one function with a list of args
that's literally the whole thing
now, as an incedental thing, there are "associative" functions which act the same with apply and reduce, that's because someone constructed the function that way
it's not a property of apply or reduce
Cool. I am looking at this example https://clojuredocs.org/clojure.core/reduce#example-542692ccc026201cdc326c40 for calculating primes and it is pretty awesome, I don't understand the (map (partial mod ...)
part although I think I know what it's supposed to do
Also, the fact that functions are designed to abstractions is a tool for experts that I think also makes a beginner’s life easier.
I need more practice using map
and partial
seems like a powerful combo
partial
is just a stylistic alternative to fn
or #()
really
(partial func fixed-arg) will return a function that is like calling func with the first argument set to fixed-arg
oh that's not the aspect I think of as expert oriented
I'm thinking of the complexity around java interop, type hinting, chunking of lazy sequences...
Oh neat. I did not think of it that way!
so a partially determined anonymous function
re-listening to that talk I linked: "instruments are made for people that can play them, but beginners are not players yet"
"should we make cellos that auto-tune?"
yeah if you look at the source code for it it's also using apply 😄
neat
"I don't think we should go to our tools and orient them to the first five seconds of usage"
I'm just wondering why in the source it is defined explicitly for up to 3 arguments
that's a performance optimization
ah interesting
it's ubiquitous in clojure's core code, to break up arg counts so the method bodies and call stacks are smaller
to avoid the apply and the concat
right
I see.. because my first instinct would be to specify just the minimum needed
right, that's good code design when you are not in a bottle neck for performance, to have the smallest simplest code you can
I’ll check out the talk. I think that’s totally fair! I just don’t think those goals are mutually exclusive. Sometimes those design choices are going to work out for both experts and beginners, and sometimes tooling can be made around expert systems that make them easier for beginners. I see a lot of people using tools like Notion and Roam at extremely advanced levels, and think those people might benefit from something that ramps them up into programmability, which should ramp people up towards expert usage
but clojure.core is in every clojure user's speed bottleneck
makes sense, thanks for the explanation
GuidedTrack code basically parses to a JSON that gets interpreted by JS, but I’m imagining something that’s generally as simple as GuidedTrack in terms of the verbs (function operators) and syntax (basically just nested stuff within a structural editor) but gives access to the full programmability of a language like Clojure
right, I'm not arguing for that approach, just warning that this is the philosophy behind clojure's implementation and it can bump up against your interests
it's a conflict I've seen multiple times, so I'm helping manage expectations up front 😄
I appreciate that 🙂️ in making learnable programming tooling, I generally consider it valuable that I’m a behavioral product designer approaching programming fresh and naive, but I definitely rely on experts to tell me where my expectations are totally out of whack
I’m trying to learn from the history of what’s been tried moreso than get discouraged by it 😅
is there a way to use conditions
as the final arg list to condp
here? the idea is that the map will be used somewhere else, too, so I’d like to avoid duplicating them
(let [conditions {1 "Only one" 2 "Truly two"}
v 1]
(condp = v 1 "Only one" 2 "Truly two"))
you'd probably enjoy this talk from strange loop 2019 about teaching programming (and what mainstream cs eduction gets wrong) https://www.thestrangeloop.com/2019/how-to-teach-programming-and-other-things.html
(conditions v)
@jeffrey.wayne.evans because it doesn't need to create a function for each example, and also because it doesn't need to create a lazy seq to provide args to some-fn
Everytime I try to use generative tests, I get stuck at the very first step which is to decide on the "properties" that I should be testing, and how I should be generating them programmatically. The fundamental question in my head is this: aren't the functions that I'm going to write to assert the properties going to be very much the same as the function that I am about to test itself?
https://grep.app/search?current=4&q=test.check&filter[lang][0]=Clojure.
https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-fall-2011/lecture-videos/MIT6_006F11_lec01.pdf. Can I use test.check
for this? Can someone think of properties that are anything other than the property that the peak is actually a peak?
(defn mid-idx [start end]
(+ start (int (/ (- end start) 2))))
(defn peak-finder-1D
([v]
(peak-finder-1D v 0 (dec (count v))))
([v start-idx end-idx]
(when (<= start-idx end-idx)
(let [mid-idx (mid-idx start-idx end-idx)
maybe-peak (get v mid-idx)
left-of-maybe-peak (get v (dec mid-idx))
right-of-maybe-peak (get v (inc mid-idx))]
(cond
(nil? left-of-maybe-peak)
[mid-idx maybe-peak]
(nil? right-of-maybe-peak)
[mid-idx maybe-peak]
(and (>= maybe-peak left-of-maybe-peak) (>= maybe-peak right-of-maybe-peak))
[mid-idx maybe-peak]
(< maybe-peak left-of-maybe-peak)
(peak-finder-1D v start-idx (dec mid-idx))
(< maybe-peak right-of-maybe-peak)
(peak-finder-1D v (inc mid-idx) end-idx))))))
ah right, since it’s simple equality this would work
the real case is more involved
(let [conditions {#"^foo.*" "Some foo" #"^bar.*" "Some bar"}
v "bar-value-1"]
(condp re-matches v #"^foo.*" "Some foo" #"^bar.*" "Some bar"))
since condp is a macro, you'd need a data structure known at compile time and another macro
you could probably do something more complex with some-fn and functions that return a result on regex match or nil otherwise
property testing is easy. the only hard parts are the properties, and the generators. 😎
https://fsharpforfunandprofit.com/posts/property-based-testing-2/
^^ that's a great article
I often look for the “Hard to prove, easy to verify” as a way to avoid rewriting the original code
ah, that’s a good idea
(let [conditions {#"^foo.*" "Some foo" #"^bar.*" "Some bar"}
v "bar-value-1"
preds (map (fn [[re result-string]]
(fn [s]
(when (re-matches re s)
result-string)))
conditions)]
((apply some-fn preds) v))
this returns "Some bar"
perhaps it's better style to bind (apply some-fn preds)
to a name in the let block
Thank you 🙂
thanks, appreciated
@kslvsunil If you’re willing to spend money on online courses, I highly recommend Eric Normand’s three courses on Property-Based Testing (beginner, intermediate, and advanced — if I recall correctly). On http://PurelyFunctional.tv
He walks you through several types of properties that you can look for in your problem space and also provides a lot of insight into how to build a “model” that you can use for comparing with your actual code, operating under property-based testing. They are excellent courses.
That was a nice article, thanks Ghadi. Will check them out Sean, thank you.
i've been wondering why those seemingly redundant definitions exist for yeeeeeeears, this makes sense now
🍻
I am fetching the data from the database where data is coming like {:empiId 1256, :state Colombo}
which is giving lazy seq
on printing type, when I try to fetch the values it giving me castexception , initially I tried convert using (into {} output)
later could not able to fetch the values of :state , can anyone help me please
the common problem when combining lazy seqs with databases is trying to realize the result after the connection has closed
> when I try to fetch the values in what way? what's the line of code and the error?
there are a lot of ways to get a ClassCastException
Hi @noisesmith, give me sometime i will try to regenerate the exception
looking at the code a second time: I think I like this reduce / reduced version beter
(let [conditions {#"^foo.*" "Some foo" #"^bar.*" "Some bar"}
v "bar-value-1"]
(reduce (fn [_ [re result-string]]
(when (re-matches re v)
(reduced result-string)))
nil
conditions))
does the same thing, some might think it's less idiomatic I guess, I think it's significantly simpler, and it does less workbecause it doesn’t need to create all those intermediate fns?
I guess this peakfinder falls in the easy-to-prove bucket and so generative testing isn’t really required.
or just use a parser that will parse alternatives and tell you which one matched
user=> (require '[instaparse.core :as insta])
nil
user=> ((insta/parser "thing=foo|bar\nfoo = 'foo'\nbar = 'bar'\n") "bar")
[:thing [:bar "bar"]]
user=> ((insta/parser "thing=foo|bar\nfoo = 'foo'\nbar = 'bar'\n") "foo")
[:thing [:foo "foo"]]
user=>