Fork me on GitHub
#beginners
<
2020-05-07
>
Chris K00:05:27

When I declare an atom variable in clojure, why do I have to deref it to get just the value? If I don't deref it, I can still see the value, but in like a more complex and verbose structure

Chris K00:05:48

BUt also when I'm using it inside the 'swap!' function, I don't deref it...so I'm not sure why is that

noisesmith00:05:10

swap! is a function, that takes a place, and puts a new content in it

noisesmith00:05:19

so you need to pass a "place" for it to make sense

noisesmith00:05:00

@foo generally (not just for atoms) takes a thing that can have a value and gets the value out of it

Breno Duque Estrada00:05:39

Atom is a data structure that holds the value "inside" them to control the consistency, concurrency, etc

noisesmith00:05:03

atoms don't need to be "variables", they are first class data and it's easy to pass it / hold it etc. which is clumsier with variables

Breno Duque Estrada00:05:38

Swap! receives a function that will be executed inside the atom

noisesmith00:05:57

it supplies a return value that replaces the content of the atom

noisesmith00:05:29

it's doesn't lock or scope the atom per se

Chris K00:05:42

oh.. I see thxs I think I understand better

noisesmith00:05:43

@sunchaesk for a more concrete example - when I do (def g (partial map f)) the var f is captured, and I implicitly use the value not the container itself

noisesmith00:05:15

this leads to weird behavior - if I change f later, I need to remember to change g

noisesmith00:05:54

there's an "opposite" to @, which is #', that lets me capture the var itself instead of the contents (def g (partial map #'f))

noisesmith00:05:12

in fact, @#'foo is the same as foo if foo is a var

noisesmith00:05:59

atoms are meant to be changed by application code at runtime, so it's easy to get the container, and an extra step to get what's inside - that's expected to happen as late as possible

noisesmith00:05:27

vars are meant to be changed by a developer while working on one's code, so it's easy to get the contents, and an extra step to get the container

noisesmith00:05:34

each one makes the "expected" use simpler

Chris K00:05:52

yeah that makes much mor sense now

Chris K00:05:05

thxs a lot

noisesmith00:05:20

in fact, an atom is often in a var - though you can also use them first class as an arg to something that uses them / captures them

noisesmith00:05:38

there's a great Rich Hickey talk about place oriented vs. value oriented programming

noisesmith00:05:49

atom is explicitly meant to be a "place"

phronmophobic00:05:16

I thought it was supposed to represent an identity. the main difference being that places (on their own) don’t follow the epochal time model.

noisesmith00:05:32

are java.util.HashMap or java.util.Date instances identities?

phronmophobic00:05:49

that’s a good point

phronmophobic00:05:00

i guess atom is really just supposed to be a reference type

phronmophobic00:05:14

with sane semantics

noisesmith00:05:36

yeah - what I tried to get at was that its fundamental utility was to be a location, and the reason we prefer it over alternatives (which I didn't get into), is the sane behavior under concurrency

noisesmith00:05:02

if I'm using identity or place here incorrectly I do want to know though

phronmophobic00:05:27

I guess it is a place, but the fact that it’s bundled with a recipe for change and perception is important (which distinguishes it from many other places)

noisesmith00:05:22

but that's opt in - if you use reset! you get none of that safety, and only as much as you'd have with a var, or a thread safe mutable hash-map

phronmophobic00:05:26

thread safety is still important, but yea. I think I jumped the gun to say it wasn’t a place for some reason

noisesmith01:05:38

if you have a document describing what makes something an identify vs. place I'd be interested, I'm not totally sure when I'd use the term identity

noisesmith01:05:51

(outside eg. what kind of equality I expect to be used)

didibus01:05:41

Hum... I don't think an identity is the same as a place. Like a place is just what it says, a place where you put things and grab things from.

phronmophobic01:05:27

identities don’t have to be places eg.

(def people-by-id
  {"1230u1203912" {:name "Bob"}
   "129u9cnu1209u12" {:name "Bob"}})

phronmophobic01:05:56

here, you have two people, with different identities, but their identity isn’t tied to a place

didibus01:05:28

Ya, exactly. An identity is just an identifier for something. The only place where there's a connection is that a thing that changes cannot be identified by its form, since its form changes over time

phronmophobic01:05:50

well, i think the identity is the logical connection of values over time

didibus01:05:18

It can be, but a place is also an identity

noisesmith01:05:23

maybe it's sort of a trio: identity / place / value - your identities are value based, you can also have place based identities (standard object identity)

phronmophobic01:05:23

it’s nice to have a reification of that logical identity (an identifier)

didibus01:05:35

The things in the place is a great way to identify things

phronmophobic01:05:58

well, any unique value can be used as an identifier

didibus01:05:59

Well, not a great way

didibus01:05:03

A bad way lol

didibus01:05:20

But I mean, a place can be an indentifier for the things at that place

noisesmith01:05:59

I nerd sniped myself into rewatching the value of values, it's a good talk

phronmophobic01:05:02

because places are unique. unfortunately, they can’t be copied which is annoying

phronmophobic01:05:15

I watched it earlier today during lunch

didibus01:05:18

I don't really know if Clojure offers a better concept of identity for things that change form over time then a place?

phronmophobic01:05:40

you can use any* identifier that’s unique

noisesmith01:05:53

I mean, if you call = on an atom, that's the only way to get true - is if it points to the same place

didibus01:05:36

Right, atoms don't carry a stable Id with them, so they are effectively identified by place no?

phronmophobic01:05:12

yes, but they’re not the only* option

didibus01:05:42

What are the others? Record and deftypes?

phronmophobic01:05:52

well, the other reference types

phronmophobic01:05:12

vars, refs, and atoms

noisesmith01:05:29

agents, volatiles

didibus01:05:38

Wouldn't all of their identity be their memory location? Aka, their place?

noisesmith01:05:12

vars are usually looked at in terms of their owning structure and name

noisesmith01:05:25

but yeah, the others are just identity based

phronmophobic01:05:47

but you can use a guid as an identifier in clojure

noisesmith01:05:13

sure - not on the language level, but on the level of convention right?

phronmophobic01:05:25

and instead of getting its current value from memory (like atoms), you could get its current value from the disk or the network

didibus01:05:46

I mean, without me creating my own. Are there any construct on Clojure who can model changing values whose identity is not based on a place?

noisesmith01:05:28

they are usually looked up by the identity of their ns, (which is looked up by name) plus their name

noisesmith01:05:52

though technically in bytecode they just get looked up by place, that's not the semantics we usually engage with

phronmophobic01:05:11

I also have no idea how refs work

phronmophobic01:05:29

since they use mvcc, I’m not sure whether that counts as having multiple places

didibus01:05:05

Hum, I don't think the lookup matters as much as the equality.

noisesmith01:05:15

I think it's more that mvcc coordinates the update of their contents, but the contents stay in one place

didibus01:05:28

Are two vars equal because they have the same name?

noisesmith01:05:27

@U0K064KQV when I use vars, I am explicitly asking for an ns by symbol, and I am explciitly looking for one name in that ns by symbol, if that goes out of sync (eg. because clojure.tools.namespace/refresh recreated the ns out from under me), I will experience that as a bug in my code and fix it by redoing the original lookup by recompiling

noisesmith01:05:49

we very rarely even check var equality, they implicitly deref

didibus01:05:38

Right, but that's a place, albeit not a memory address, but it's the same thing, it's just an address in the namespace map

phronmophobic01:05:40

@U0K064KQV, i’m not sure what you’re getting at. logically, identifiers don’t have to be places. I’m not sure if identifiers are only implemented as places in “clojure” (does using java libs count? what’s wrong with using a library?).

didibus01:05:07

The way I see it it's like: {:a 10} and {:a 10} are the same thing because they have the same value. So no matter what place you put them in, they are the same thing

noisesmith01:05:30

@U0K064KQV it's not a place, because it's looked up by tokens that we assign meanings to, not by location in memory

noisesmith01:05:10

(in terms of program logic at least - of course in bytecode most of these things become places...)

phronmophobic01:05:30

and from a technical perspective, the jvm is allowed to move stuff around in memory. the OS is also allowed to move stuff around in memory without telling us too

didibus01:05:45

But if you want to model something that changes like: {:a 10} and {:a 20} now what? If you say these are both the :amount-of-money-in-my-bank-account

didibus01:05:36

So one way is to agree on a place where we will put the amount of money in my bank account

didibus01:05:12

And now we can say, whatever is in that place must be the :amount-of-money-in-my-bank-account

noisesmith01:05:12

or to arrange ot look it up by a known identifier

didibus01:05:45

I'm not talking about how we find it, just about it's identity

noisesmith01:05:15

in c we would be using places explicitly for everything but primitives, but we have other conventions in our language semantics that aren't place oriented

didibus01:05:26

In this approach the identity is now the place it is in

noisesmith01:05:44

as far as my program is concerned clojure.core/+ is an identity, despite the fact that reloading clojure.core could change its place

phronmophobic01:05:58

you can use places for identity, but you don’t have to.

noisesmith01:05:01

and that identity is based on the symbols I use to look it up

didibus01:05:26

Ya I know, I'm just saying that one way to create an identity for something that changes. The other way is to put a tag on the thing

didibus01:05:53

But if the tag is not on the thing, then in my view it is still then that the place is the identity

phronmophobic01:05:22

not all places are identities

didibus01:05:09

Ok, so maybe I've lost everyone :p

phronmophobic01:05:23

places can be identifiers, but they don’t have to be

didibus01:05:46

Let's take atom again. It is the place of the atom that tells me what the value inside it refers too

didibus01:05:09

(def my-money (atom 10))

didibus01:05:20

my-money is a place

phronmophobic01:05:06

atoms are an example of using a place as an identity

phronmophobic01:05:32

but there are are also examples of places that aren’t identities

didibus01:05:04

Ya ya agree

🍻 4
didibus01:05:39

I guess my point is, I don't think there are any standard construct in Clojure that are basically a stable Id + a mutable section

didibus01:05:51

Bundled together

didibus01:05:10

The stable id is normally implicitly the place where we put the mutable section

phronmophobic01:05:35

there’s enough stuff in clojure, that I can’t say for sure

phronmophobic01:05:46

but I think vars might be a counter example to taht

phronmophobic01:05:56

like when do (def foo 42)

phronmophobic01:05:09

I think it might create an actual new place

didibus01:05:14

It depends what you want to model? Like 42 is 42 because of its value, not because it is found in foo

phronmophobic01:05:09

i’m not talking about 42, I’m talking my.namespace/foo

didibus01:05:13

But if foo is something that changes value over time, and 42 is supposed to be a foo and not the value 42, then foo would be the identity as well no?

phronmophobic01:05:41

foo’s value is currently 42, but it might change to -42

didibus01:05:46

The symbol itself?

didibus01:05:54

Or the var it points too?

phronmophobic01:05:25

the var it points to

didibus01:05:14

Hum.. ya I thought the var was a bit similar to an atom, just not thread safe

phronmophobic01:05:18

I would say my.namespace/foo ’s identifier is my.namespace/foo, not whatever its memory address is

didibus01:05:18

I know two symbols of same name and namespace arn't equal

didibus01:05:29

I don't know how equality of vars work

didibus01:05:37

Def does intern the var

didibus01:05:46

So okay maybe you guys are right about Vars

didibus01:05:58

> This means that, unless they have been unmap-ed, Var objects are stable references and need not be looked up every time. It also means that namespaces constitute a global environment in which, as described in Evaluation, the compiler attempts to resolve all free symbols as Vars.

noisesmith01:05:03

also, outside of -128...127 for integral types, numbers are never places, only values

didibus01:05:20

I guess you could consider an interned var to be a combination of a stable id, the symbol's namespace+name and the possibility changing value contained in the Var

didibus01:05:47

Given a var, can you find it's symbol though?

didibus01:05:43

I think that's my criteria for if identity is a place or not 😝. If you take the thing and move it somewhere else, can you still know what's its identity? If you can't, then the place was acting as the identity, and having lost the knowledge of where the thing was, so have you lost its identity. If you can, then the identity is within the thing itself, and not based on where it was.

phronmophobic01:05:22

given a var, you can find its symbol

noisesmith01:05:14

=> (.sym #'+)
+

didibus01:05:12

Okay, did not know that. So I guess vars carry their identity on them

didibus01:05:26

So they are not place oriented :p

noisesmith01:05:52

not only that - standard idiomatic language constructs find them by that name

didibus01:05:12

Ya, but I honestly don't really see that as being different. If I said, ok, this function will always be at memory location xyz. Or, okay this function will always be at key xyz in namespace map

noisesmith01:05:05

the difference is that one of those is the normal path for finding a var, and the other is a rare special case

didibus01:05:06

It still feels like we're doing place oriented programming. Because now everything relies on this implicit agreement and knowledge of where things are going to be and what each thing at each place is supposed to represent

didibus01:05:43

And if someone messes up, and doesn't put the right thing in the right place, everything breaks

phronmophobic02:05:54

i agree that vars are still place oriented. “any time new values overwrite old values, you’re doing PLOP”.

phronmophobic02:05:40

but hopefully, your whole program isn’t all about places

didibus02:05:44

True, also I think places that are at least consistent throughout the whole execution are much less brittle and easier to deal with. And most idiomatic Clojure never changes the value of a Var after the first time it is defined

noisesmith02:05:11

until you ask it to (ns-unmap) use a tool that does that for you (ns/refresh)

noisesmith02:05:09

clojure has "two sets of rule" - for dev vs. runtime, and I think that's an important quality

noisesmith02:05:41

one of those rules are a very strong convention and not rules per se, but luckily the conventions are strong enough

💯 4
jaihindhreddy04:05:46

Going by Are We There Yet, a name for something that changes in atomic successions and is perceivable by others without stopping anyone is what I'd call an identity. And in order to make something (with computers) that represents an identity, then we need values. An identity is a label we (humans) give to a sequence of causally-related values. Which means values are the more primitive thing. Whereas with a mutable datastructure, all you have is a place, it's neither an identity (in the sense described above), nor a value.

phronmophobic05:05:48

from https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/SimpleMadeEasy.md, > Objects complect state, identity, and value. while we would like to represent identities using references that obey the epochal time model, objects can still be used to represent identities (although this is ill advised).

phronmophobic05:05:24

and just because you can use an atom to represent an identity. you could also misuse it to represent something besides an identity. so a construct that follows the epochal time model does not imply that the construct must represent an identity

💯 4
Chris K00:05:10

Nice I will check this out

ryan echternacht01:05:22

@sunchaesk that talk is what brought me to clojure. It’s REALLY good

Chris K05:05:54

yeah I've heard others saying that this talk is great. I'll make sure to check it out

andy.fingerhut07:05:17

If you prefer reading to watching/listening, transcripts of many talks available here. Also good for searching for key words later if you want to find something you remember only one word or two of: https://github.com/matthiasn/talk-transcripts

Chris K08:05:58

Thxs for the transcript @andy.fingerhut

Chris K08:05:18

I also had a question about functional programming while watching the talk by Rich Hickey Value of Values He said that one thing people are pleased about is that you can share the values freely without having to worry I get the part that talks about how you don't have to worry because the value is immutable, but I don't understand why it is only functional programming that allows you to share data freely and not same between OOP and FP

Cameron09:05:04

Do you mean why does OOP not use immutables, or what is it about OOP that makes sharing hard compared to FP, or something else

Cameron09:05:26

(this is a tangent I'm about to get on, not an answer to your question) Personally, I don't think mutability vs immutability should be baked into the definition of OOP -- it mostly is, but to me its mutable as a historical detail, not because it has to be. To me, a language with data and functions tightly bundled, and all the other aspects of OOP, but where each function returned a new, updated object, instead of updating some living piece of memory somewhere, would still be OOP -- it is the object that's important (and fortunately I could maybe even argue that in any flame war this sparks, since its right in the name). But of course, then we're just talking about language, instead of the ideas behind them, and what ideas the language should refer to -- and when people say OOP, they are referring to programming built on mutation. And why wouldn't they? I know of no real immutable OOP, although I've done a lot of experimentation there myself

Cameron09:05:14

Anyways, in my personal language, imperative programming is what I call a language that expresses its change as side effects, and declarative programming one that expresses it as a fresh return value of some data (thereby having the data only represent, or describe, what will become a change), and they're a much lower level property than being, say, 'OOP'

Chris K09:05:04

Right yeah I think what you said is more accurate definition of these paradigms

noisesmith11:05:12

@zdot101 I'd suggest that even in Clojure (or more extreme, even in scheme or forth) functions and data are tightly coupled - it's just that strategically, less work is put into proliferating data types and more into proliferating the number of functions on the small set of data types

👍 4
Cameron12:05:33

Ah, but I said tightly bundled . Granted, I'm trying to think now of a more rigorous definition to differentiate the two, but I think the main difference is I was referring to was not their coupling (although bundling itself couples), but to the fact that they are an atomic unit; when you deal with objects, you don't get them separate. Of course, as you dig into that, it gets trickier -- being 'together' has less meaning as you zoom in and get more rigorous, and a more fitting way of explaining that sort of thing is that they are still individual things, but with a different set of relationships drawn between them than, say, functions and data in Clojure.

potetm12:05:41

I think there’s a fair argument to be made that Erlang is an example of an immutable OOP.

potetm12:05:17

(If anyone says “Scala,” I’m gonna flip this table.)

4
Mattias12:05:25

This is really interesting. It annoys me to no end (and I really believe it harms the chances of getting anyone to understand) when people use the terms OOP and, equally, FP in daily use.

Cameron12:05:34

scal..lops

Mattias12:05:55

Good example is anyone, ever , saying “look, in FP we have map/reduce and for loops are bad”. It’s just bizarre to think that was ever a real point. Luckily, today all languages have it either in the language or via libraries so we’re past that, but still.

Mattias12:05:04

And... for people writing testable code, doing a larger and larger part of their code as pure functions have been standard thinking in corporate Java land for a long time now.

Mattias12:05:35

So... there are lots of interesting discussions to be had, but not hidden behind “FP” and “OOP”.

Mattias12:05:58

Last part of the rant - I really like Eric Normands podcasts (hmm, long time now?). He is good at the nuances and degrees but also at times falls for the easy trap of saying “ it in OOP it’s like this”.

Kris C13:05:09

👋 I’m here! What’d I miss?

Cameron13:05:02

Chris K stole your name, but is dyslexic

Cameron13:05:07

I think that's it cap

practicalli-john15:05:13

Is the clojre.core/inst? simply checking if its argument is an instance of a Java object (assuming I am on the JVM). Just wanted to confirm my assumption. I looked at the source code but was no wiser 😞 https://clojuredocs.org/clojure.core/inst_q

dpsutton15:05:18

from my reading it checks to see if the argument satisfies Inst a protocol. and (-> Inst :impls keys) lists java.util.Date and java.time.Instant as two implementers of this protocol

andy.fingerhut15:05:18

And others may choose to extend the Inst protocol to other types in their programs, or for time-related types they create.

practicalli-john15:05:04

I am not clear if all java objects satisfy the Inst protocol. If it is just specific Java classes (or other types) I guess I can test them as I make use of them in applications I write. But then curious as to why any type would implement the Inst protocol if its quite selective.

ghadi15:05:42

the protocol doesn't care, and it's not generically possible to say "what are all the implementors of this protocol?"

ghadi15:05:21

if you're checking for specific java classes, use (instance? java.util.Date x)

andy.fingerhut15:05:59

You can check all of the JVM classes the implement the Inst protocol at this moment using the technique that dpsutton showed, true?

alexmiller16:05:50

this doesn't cover metadata protocol extensions

ghadi15:05:13

dunno what that technique was

ghadi15:05:31

@jr0cket inst? seems more useful for predicates with clojure.spec

andy.fingerhut15:05:35

(-> Inst :impls keys) from several mins ago

ghadi15:05:52

(it is antithetical to protocols to have to check a type, then use the protocol)

ghadi15:05:43

inst? doesn't tell you anything about what you can do with the object, except call inst-ms on it

andy.fingerhut15:05:00

I am not saying you want to dig into the internals that expression uses in lots of production code. Just if you are curious and doing some poking around/debugging at the REPL, wanting to know what is going on.

practicalli-john15:05:13

I am looking at predicates with clojure.spec right now, but it the value of a type an instance is still to click. I assume its so that you can satisfy the predicte, it seems highly generic though

ghadi15:05:14

but most date/time needs in app code are broader than grabbing unix epoch millis

practicalli-john15:05:05

Ah, i missed the (-> Inst :impls keys) code from dpsutton, as it wasnt highlighted... 🙂

practicalli-john15:05:28

its very useful, thanks

ghadi15:05:29

that doesn't tell you reifies or things that directly extend the protocol

ghadi15:05:55

just out-of-band stuff like using extend-*

ghadi15:05:40

(it will not show reify / deftype / defrecord extensions of that protocol)

andy.fingerhut16:05:06

understood. It is at best a partial list of JVM classes that implement the protocol.

ghadi16:05:58

it is a complete list of out-of-band extensions

ghadi16:05:17

which may be supertypes of compatible classes

bfabry16:05:25

(also undocumented and may go away with a version change)

ghadi16:05:35

(like if you extended a protocol to CharSequence, you'd automatically get String)

practicalli-john16:05:09

I have a much better context as to where this fits in (although still need some hammock time around the why aspect). Thanks all.

ghadi16:05:12

generally, i do not recommend using inst? like this: (if (inst? x) then else)

ghadi16:05:56

there is a spec generator hooked into the inst? predicate, and it returns a generator of java.util.Dates

ghadi16:05:06

clj -A:convenient
Clojure 1.10.2-alpha1
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (require '[clojure.spec.gen.alpha :as gen])
nil
user=> (s/gen inst?)
#clojure.test.check.generators.Generator{:gen #object[clojure.test.check.generators$such_that$fn__558 0x78d6447a "[email protected]"]}
user=> (gen/generate *1)
#inst "1970-01-03T09:07:10.724-00:00"
user=> (class *1)
java.util.Date

Wilson Velez20:05:12

which is the easiest way to format a js/Date?

Wilson Velez20:05:30

I did it using moment.js (.format (moment %) date-format)

alidlorenzo20:05:14

I’d avoid moment.js since its API is mutable. for a js lib I prefer date-fns, but there might be good cljs libs too (haven’t had chance to try them out yet)

noisesmith20:05:41

there are also a few functions in goog DateTime, but not a proper formatter https://google.github.io/closure-library/api/goog.date.DateTime.html

Wilson Velez14:05:00

thanks @UPH6EL9DH and @U051SS2EU, I’ll take a look

Wilson Velez20:05:30

I did it using moment.js (.format (moment %) date-format)

tvalerio21:05:41

Hi I have this vector

[{:key "document_env", :value "teste"} {:key "document_env2", :value "teste2"}]
How can I transform into a map like this:
{"document_env" "teste" "document_env2""teste2"}
?

andy.fingerhut21:05:49

There are multiple ways to do it, but this is one:

user=> (def d1 [{:key "document_env", :value "teste"} {:key "document_env2", :value "teste2"}])
#'user/d1
user=> (into {} (map (juxt :key :value) d1))
{"document_env" "teste", "document_env2" "teste2"}

andy.fingerhut21:05:04

This is another:

user=> (into {} (for [m d1] [(:key m) (:value m)]))
{"document_env" "teste", "document_env2" "teste2"}

tvalerio21:05:18

Thanks a lot @andy.fingerhut! Didn’t know this function: juxt 🙂

andy.fingerhut21:05:03

NP. juxt is certainly not necessary to achieve the result, of course, but sometimes nice to see multiple ways to do the same thing.