Fork me on GitHub
#clojure
<
2016-06-07
>
ag01:06:19

how do I filter out from collection by given kv pairs? e.g. from:

({:id 1, :f “a”, :x “foo"} {:id 2, :f “a”, :x “bar"} {:id 3, :f “b”, :x “foo"} {:id 4, :f “c”, :x “bar"})
if given {:f “a”} or {:f “a” :id 1} should return the first el

nathanmarz01:06:41

@ag use select-keys and =

iku00088801:06:39

I found this mentioned about clojure keywords "They cannot contain '.' or name classes.". Does naming a class mean it cannot be used for a name of a class?

iku00088801:06:29

I am a bit puzzled why classes are mentioned in a clojure context.

ag01:06:55

@nathanmarz: yeah, ended up using filter = vals,`keys` and select-keys… sorry, being lazy

fugalfunkster01:06:05

(= ( reduce (fn [x y] (if (= (first (last (quote x))) y)
                      (conj (pop (quote x)) (conj (last (quote x)) y))
                      (conj (quote x) (list y)))) [1 1 2 1 1 1 3 3]) '((1 1) (2) (1 1 1) (3 3)))

fugalfunkster01:06:52

oops, posted that too soon, anyway, I'm working through the 4clojure exercises and I'm having a hard time with the function I wrote for my reduce

fugalfunkster01:06:17

it should read:

seancorfield01:06:32

I’m guessing you don’t want all those quote calls in there?

fugalfunkster01:06:46

(= ( reduce (fn [x y] (if (= (first (last (quote x))) y)
                      (conj (pop (quote x)) (conj (last (quote x)) y))
                      (conj (quote x) (list y)))) [] [1 1 2 1 1 1 3 3]) '((1 1) (2) (1 1 1) (3 3)))

fugalfunkster01:06:34

Howdy seancorfield! When I step through the if block manually, the function seems to work fine. The boolean test is true when I think it should be true and false when I think it should be false. And, on their own, each of the branches return what I would expect. But when I nest the if block in my function I get IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol clojure.lang.RT.seqFrom (RT.java:528)

seancorfield01:06:52

Well (quote x) produces the symbol x — it does not evaluate x.

seancorfield01:06:06

So those four quote calls are almost certainly wrong.

seancorfield01:06:43

And it sounds like this might be a better conversation for #C053AK3F9 so that other folk learning Clojure may benefit from this discussion?

fugalfunkster02:06:01

Ok, that would explain why my manual input of a collection produced desirable results. Thanks seancorfield, I will head over to the beginner forum after reconsidering my use of quote.

fugalfunkster02:06:56

Ha, I deleted every instance of quote and the tests passed; now to understand why. Thanks again seancorfield!

seancorfield02:06:14

Happy to help! 4clojure is addictive.

bcbradley02:06:48

hey guys i need some help applying a function

bcbradley02:06:08

what i want is (apply -> entities [ SOME STUFF])

bcbradley02:06:12

but -> is a macro

Alex Miller (Clojure team)02:06:19

lift it into a function

Alex Miller (Clojure team)02:06:24

something like that maybe

bcbradley02:06:35

let me explain in detail exactly what i'm trying to do

bcbradley02:06:44

maybe i'm going about it all wrong and you can point me in the right direction

bcbradley02:06:01

i'm using play-clj to make a game

bcbradley02:06:22

i have some entities (which is just a vector of maps)

bcbradley02:06:03

i want my entities to be smart

bcbradley02:06:11

really smart. i want them to be able to do their own behavior

bcbradley02:06:36

and i want that behavior to not come with any assumptions about the structure of the program / it shouldn't make any assumptions in general about what that functionality depends on argument wise

bcbradley02:06:46

which means every function takes the entire entities vector as an argument

bcbradley02:06:14

the entities vector might look something like this:

bcbradley02:06:23

[{... :name "default" :type "head" :health 100.0 :power -10.0 :weight 5.0 :ammo 15 :texture "player1.png" :update do-nothing} ...]

bcbradley02:06:39

do-nothing is a function

bcbradley02:06:46

perhaps: (defn do-nothing [entities] entities)

bcbradley02:06:58

it could be anything though

bcbradley02:06:12

it could totally erase all the entities in the game

bcbradley02:06:15

or flip them upside down

bcbradley02:06:21

no assumptions about what it might do.

Alex Miller (Clojure team)02:06:10

if you want to build compound transformations over entities, then maybe transducers would be a good fit

Alex Miller (Clojure team)02:06:19

better than trying to monkey with ->

seancorfield02:06:33

So the input to each "smart function" is the world (the vector of entities) and the output of each "smart function" is… also the world?

bcbradley02:06:41

yes it sounds terrible

bcbradley02:06:44

i know its n^2

bcbradley02:06:46

but i don't care

bcbradley02:06:21

in the end if performance is an issue i'll have entities responsible for being a "middle man"

bcbradley02:06:35

that would just provide the performance desired as a feature

bcbradley02:06:43

like, a collision detection "middle man"

seancorfield02:06:55

Let’s just see if I’m on the same page as you: you start with a vector of entities, for each entity in the vector, you apply its :update function to the world to produce a new world, and you keep going though the original world’s entities until they’re all done, then you start again with the new world?

bcbradley02:06:55

to get the function to apply you'd have to say ((:update entities) entities)

seancorfield02:06:21

Well (:update entity) singular since entities is a vector 🙂

bcbradley02:06:39

this is my render function

bcbradley02:06:41

:on-render (fn [screen entities] (clear!) (render! screen entities)

bcbradley02:06:48

it should return a vector of entities

bcbradley02:06:46

the thing i'm trying to do is a bit confusing i guess

seancorfield02:06:05

So my question is: what happens if you’re processing entity 4 of 20 and it removes entity 5 — do you still run entity 5’s :update on the world so far (i.e., at the start of this update phase)?

bcbradley02:06:15

thats a good question

bcbradley02:06:35

at this point i know the overall picture of what i want, but don't care about details very much

bcbradley02:06:41

i can create around limitiations

seancorfield02:06:50

Or, similarly, if entity 4 removes entities 0, 1, 2, 3, 4 — how do you know what is the next entity to run?

bcbradley02:06:16

if i had it anyway i wanted, ideally

bcbradley02:06:42

the entities argument for the render function would provide a set of entities

bcbradley02:06:47

and i would loop over those and only those

seancorfield02:06:50

My sense is that you need to let every entity try to update the world in order, then start over with the end result of that...

bcbradley02:06:53

for functionality

bcbradley02:06:59

but the entitiy arguments

seancorfield02:06:02

In which case your update phase is just a reduce...

bcbradley02:06:20

but the entity arguments to each "update" function of each entity

bcbradley02:06:24

would be threaded from the last

seancorfield02:06:27

(reduce (fn [world entity] ((:update entity) world)) entities entities)

bcbradley02:06:51

well that looks good too

bcbradley02:06:05

there are alot of ways to do what i'm thinking of, but they each have subtle differences

bcbradley02:06:42

let me try to parse what you've written carefully

seancorfield02:06:59

So you start with entities as your world, you walk through every entity (in the "original" world) and let it update the "current" world and when every entity has had its chance, you have a "new" world — and you run that process again.

bcbradley02:06:16

so rather than accumulate in a reduce you are replacing

seancorfield02:06:49

The reduce is accumulating the "effect" of running each entity’s :update function.

bcbradley02:06:04

the two entities at the end are confusing me

bcbradley02:06:26

would this work?

bcbradley02:06:26

(reduce (fn [world entity] (:update entity)) entities)

seancorfield02:06:28

Your accumulator is the whole world.

seancorfield02:06:59

Each entity runs its :update function on the (current) world to produce a (new) world.

seancorfield03:06:12

The world starts out as entities.

seancorfield03:06:50

Does this make it clearer? (let [world entities] (reduce (fn [w e] ((:update e) w)) world entities))

bcbradley03:06:23

my confusion isn't the binding

bcbradley03:06:37

its that world is technically a vector of entities

bcbradley03:06:44

and it just keeps getting replaced

seancorfield03:06:50

That’s what you said, yes?

bcbradley03:06:08

but i'm having a hard time understanding why i should need the 3 argument reduce

seancorfield03:06:24

Because the initial "world" is your vector of entities

bcbradley03:06:48

there aren't 2 "worlds" to take

bcbradley03:06:15

i don't have a vector of worlds

bcbradley03:06:18

i just have the one

seancorfield03:06:29

var world = entities;
for ( var entity in entities) {
    world = entity.update( world );
}

bcbradley03:06:57

yeah that looks like what i want

bcbradley03:06:01

but yeah i get it now

seancorfield03:06:07

That’s what the reduce is doing.

bcbradley03:06:07

we need to seed the value of reduce with something

bcbradley03:06:28

srry for that

seancorfield03:06:52

Sometimes the accumulator for reduce is non-obvious.

bcbradley03:06:45

the part that i got hung up on was thinking that if i didn't tell reduce what the "seed" should be it should assume that its the entire world

bcbradley03:06:48

but it won't

bcbradley03:06:55

it would take the first and second entitiies

bcbradley03:06:11

i dont' know why i assumed that

bcbradley03:06:44

as a side note, do you think any of this is a good idea?

bcbradley03:06:00

or do you think its lazy?

bcbradley03:06:27

woah sean thanks alot this helps A TON

bcbradley03:06:32

jeez that simplifies a lot of code

bcbradley03:06:45

reduce is a lot more flexible than i thought it was

seancorfield03:06:48

Cool. Glad that helped.

bcbradley03:06:10

my programming buddies are telling me that designing anything with an expected n^2 performance complexity is a recipe for disaster

bcbradley03:06:45

i tell them optimization is the root of all evil, but even as I say it I have second thoughts

bcbradley03:06:02

making a number of worlds dynamically equal to the number of entities in the world...

bcbradley03:06:20

i don't know if modern computers can even handle that for a modrate number of entities

seancorfield03:06:24

At any point you’ll only have two worlds: the one that starts this update phase, and the current accumulated-so-far world.

bcbradley03:06:45

yeah the memory is constant

bcbradley03:06:49

but the computation is n^2

bcbradley03:06:19

oh wait i think i'm not thinking of that correctly

bcbradley03:06:46

i don't actually have to make copies of the worlds because clojure uses persistent data structures right?

bcbradley03:06:53

it just shares them

bcbradley03:06:36

i'm new to clojure and functional programming

bcbradley03:06:49

reduce is O(n)

bcbradley03:06:05

its hard to break out of the state oriented frame of mind

bcbradley03:06:13

a lot of my intuition needs reprogramming

seancorfield04:06:31

reduce is O(n) so your update phase will be anything from O(n) to O(n^2) depending on what your :update functions do...

seancorfield04:06:19

And, yes, the hardest thing to shift when you learn FP is "forgetting" the state-mutating OOP mindset. Even loops are different.

agile_geek07:06:06

@seancorfield: that's one of the reasons I thought Clojure was a good language to learn FP with (unlike Scala) as it makes it quite 'unnatural' to mutate state (and explicit when you do)

plexus10:06:43

is it possible to have a macro only for the catch part of a try/catch?

plexus10:06:12

(defmacro catch-pg-key-error [[field details] & body]
  `(~'catch org.postgresql.util.PSQLException e#
     (let [sem# (.getServerErrorMessage e#)
           det# (.getDetail sem#)
           [_ field# _] (re-find #"Key \((.*)\)=\((.*)\) already exists." det#)
           ~field (or field# :no-field)
           ~details det#]
       ~@body)))

plexus10:06:34

is giving me Unable to resolve symbol: catch in this context

plexus10:06:37

the idea is to use it like

(try
  ,,, ; some stuff that might cause a duplicate key error
  (catch-pg-key-error [f d]
    (println "field" f "caused" d)))

bronsa10:06:46

no, you'll have to write a macro that wraps the outer try

bronsa10:06:45

catch isn't a special form, try is. -- try looks for sequences starting with catch in its body without doing any macroexpansion

plexus11:06:06

alright, thanks. That's good to know. I ended up just wrapping the stuff inside the catch

magnus14:06:59

Is there a way to convert a function (reference to it) to a string?

magnus14:06:36

(fn-name select-keys) => “select-keys"

gfredericks14:06:20

not really; many functions don't have names in that sense anyhow

gfredericks14:06:09

if you're controlling where the functions come from, you can use #'select-keys instead of select-keys and can convert that to a string more easily (e.g. (subs (str v) 2))

nwjsmith14:06:36

(name 'select-keys) ;; => "select-keys"

caio14:06:41

actually, you can return the string from the symbol

sparkofreason16:06:33

There are some obvious Clojure idioms which map to various monads like maybe, list, etc. Is there an idiomatic way to express the sort of thing you would do with the State monad? I'd like to be able to write computations which thread through some state without explicitly passing and returning it from every function.

gfredericks16:06:30

dave.dixon: what's the broader use case?

zane16:06:37

@dave.dixon: Something like pairing update-in with comp?

sparkofreason16:06:50

Threading a sequence of random numbers through various computations, like sampling from more complex distributions. Sometimes they take an unknown number from the sequence. Would like to make it repeatable for testing.

gfredericks16:06:13

splittable random number generators!!!

gfredericks16:06:39

⇑ use one of those and you don't need the state monad anymore

Lambda/Sierra17:06:25

@dave.dixon: stylistically, the -> and ->> macros often fill the role of a State monad.

sparkofreason17:06:11

@stuartsierra: I think I need something which will let me bind to intermediate results, i.e. I need to thread the random sequence through all operations, but in some cases I need to pull out the result of some calculation in the middle and input it to other steps.

sparkofreason17:06:22

@stuartsierra: and it's nice when done in the monad, since the random sequence part is abstracted away, and samplers can be composed hierarchically without having to directly manage the state part.

gfredericks17:06:52

with splittable RNGs you just have to pass the RNG down the stack, not back up

gfredericks17:06:08

works much better with laziness and other tactics

sparkofreason17:06:03

@gfredericks: Yeah, that would be nice. Going to play around with it a bit and see how it shakes out.

sparkofreason17:06:01

@gfredericks: Could you point me toward any particularly good examples of this use-case? I assume they're floating around in test.check somewhere...

gfredericks17:06:28

test.check probably doesn't have any pithy examples of :/

sparkofreason17:06:50

Seems like these should compose nicely, right? E.g. I can build a splittable generator of normally distributed numbers from a splittable uniform, and splitting the normals just means I make two new instances splitting the uniforms.

gfredericks17:06:49

yeah I think that's right

gfredericks17:06:30

just treat the RNG as an opaque object that you can split and have a function rng->normal or rng->normals

gfredericks17:06:43

(to return one normal or an infinite seq of them, whichever is more useful)

Alex Miller (Clojure team)17:06:36

I use "oops" for most of my passwords

Alex Miller (Clojure team)17:06:47

this isn't logged is it?

ddellacosta17:06:23

totally not logged! Also, when's your birthday? Will send you a present

devn17:06:57

Hi, first timer on Slack here! My name is Devin and my mother's maiden name is Faulkner, my first pet's name was Gertrude, and my high school mascot was the Condor. Looking forward to getting to know all of you!

sparkofreason17:06:02

@gfredericks: Out of curiosity, why can't you split just by taking a number from one sequence and using as the seed for the new one? I assume there's some reason that breaks down.

bbloom17:06:41

my favorite new feature in clojure 1.9: boolean? predicate - hurray! 🙂 i’ve written that dopey function 200 times

Alex Miller (Clojure team)17:06:11

I wrote it 200 times while we were implementing spec alone

sparkofreason18:06:44

@gfredericks: Never mind, just realized that's a silly question, since the split sequences in that case would basically just be copies of each other, delayed.

bronsa18:06:20

@alexmiller: what's the rationale for including bytes? and not i.e. longs??

hiredman18:06:04

my guess would be: byte arrays are way more common than long arrays, so while playing with an implementing spec, they needed a bytes? function so it got added, and a longs? function never came up

gfredericks18:06:35

dave.dixon: that's a common question, and it's not guaranteed to be a terrible idea but you have to assume that the RNG was designed to be used that way

gfredericks18:06:03

a splittable RNG is essentially an RNG that's explicitly designed for that use case

Alex Miller (Clojure team)18:06:31

but that doesn't preclude a possible future addition for other array types. I didn't actually participate in a lot of the decision process for this as I was on vacation last week so there may be other reasons too, not sure.

bronsa18:06:02

btw as much as I'm loving all the new features coming in 1.9, I'm probably going to hate forever s/def, IMHO s/spec! would be a much better name

bronsa18:06:49

.. or even s/def! :) </nitpick>

john.carnell18:06:54

Hey guys I am using version 0.11.0 of Specter. Everytime I try to run my tests with midje 1.8.3 I get an error message: java.lang.RuntimeException: No such var: specter/transform. However, I do not get this error when I am running my code in ring. Anybody run into this before?

hiredman19:06:40

midje :face_with_rolling_eyes:

nathanmarz19:06:13

@john.carnell: what namespace is "specter" referring to there?

john.carnell19:06:37

specter/transform

nathanmarz19:06:47

that's not a namespace

nathanmarz19:06:55

is it com.rpl.specter or com.rpl.specter.macros?

john.carnell19:06:08

hold on one moment

john.carnell19:06:25

[com.rpl.specter :refer :all]

john.carnell19:06:02

Thats what we have been using to import in all of our namespaces using specter

nathanmarz19:06:06

all the select/transform/etc. operation got changed to macros and moved to the com.rpl.specter.macros namespace in 0.11.0

john.carnell19:06:30

So I should use: com.rpl.specter.macros

nathanmarz19:06:39

I'm surprised any of your code would be working without making the appropriate updates

nathanmarz19:06:48

you need both

nathanmarz19:06:05

com.rpl.specter contains the function versions of those operations as well as all the navigators

john.carnell19:06:28

Cool. So I should require both namespaces while working.

john.carnell19:06:01

Cool. Let me give me a shot. BTW: Love this library. I showed it to my team this morning and it has been spreading like wildfire 🙂

bronsa19:06:05

@nathanmarz: can't remember if I already asked you this but -- it would be a bit of an abuse of the feature, but couldn't you make those functions :inline and have the macro and the function version in the same var rather than making them macros in a separate namespace?

nathanmarz19:06:52

well the separate namespace thing is for clojurescript

nathanmarz19:06:57

i've never used :inline

nathanmarz19:06:00

how does it work exactly?

bronsa19:06:34

I'll show you an example, one sec

bronsa19:06:36

user=> (defn foo {:inline (fn [& args] `(inc 1))} [& args] 3)
#'user/foo
user=> (foo (println "FOO"))
2
user=> (apply foo [(println "FOO")])
FOO
3

bronsa19:06:59

direct calls of foo will be routed through the inline version, HOF ones to the fn version

nathanmarz19:06:37

how is this related to definline?

bronsa19:06:10

definline is sugar over :inline

nathanmarz19:06:34

I'll look into it, thanks

bronsa19:06:10

@nathanmarz: keep in mind that this is a somewhat internal feature, don't know if it's documented anywhere

john.carnell19:06:26

@nathanmarz I am still getting the same error when I run my tests. Here is my (ns declaration) . Am I doing this correctly?

john.carnell19:06:34

(ns edge-provisioning.handlers.v2.get-edges-state (:require [compojure.api.sweet :refer :all] [compojure.core :excludes [defroutes]] [inin-clojure.logging :as log] [edge-provisioning.provisioners.provisioners :as provisioners] [edge-provisioning.utils.constants :refer :all] [edge-provisioning.utils.general :as general] [edge-provisioning.utils.constants :refer :all] [edge-provisioning.utils.properties :as properties] [com.rpl.specter :refer :all] [com.rpl.specter.macros :refer :all]) (:use [slingshot.slingshot :only [throw+ try+]]))

john.carnell19:06:32

We are only have the java.lang.RuntimeException: No such var: specter/transform error when we run our midge-based tests

nathanmarz19:06:05

@john.carnell: this is clojure right, not clojurescript?

hiredman19:06:31

you don't have an alias setup in the ns form for specter/

hiredman19:06:54

are you literally using the symbol specter/transform in your test?

john.carnell19:06:08

No we are not even writing tests yet for the code.

john.carnell19:06:17

We were just playing with it and hitting an endpoint

john.carnell19:06:29

I found this while trying to research the issue: https://github.com/nathanmarz/specter/issues/112

nathanmarz19:06:52

midge is using specter

nathanmarz19:06:58

and you're overriding the dependency

nathanmarz19:06:11

it's using the old API where transform was in com.rpl.specter namespace

nathanmarz19:06:38

midge needs to be updated to work with 0.11.0

nathanmarz19:06:16

updating is really easy, just requires namespace changes

nathanmarz19:06:32

or if it's also defining its own navigators, some straightforward name changes

john.carnell19:06:36

Ok. Ill see if they made the change, I will see if I can do a PR

nathanmarz19:06:23

it looks like the dependency is through the structural-typing library https://github.com/marick/structural-typing/blob/master/project.clj#L22

ibarrick21:06:34

is there a way to dynamically generate the bindings vector for the

for
special form, outside of wrapping the whole thing in a macro?

hiredman21:06:06

no, but it is also weird that you want to, what are you doing?

hiredman21:06:40

"dynamic" is also not the word you want here, you want "at runtime" or "at compile time" or "at macro expand time"

ibarrick21:06:58

I'm admittedly trying to be clever out of laziness, but it feels like I am missing something nonetheless. I'm trying to get the exact behaviour of "for" but the number of bindings isn't known until the function is exected.

ibarrick21:06:12

And alright noted: in this case "at runtime"

hiredman21:06:18

so use a map

hiredman21:06:52

clojure's lexical environment is fixed at compiled time, if you want to do something like that at runtime, you want a map

ibarrick21:06:03

I'm not sure how a map solves my problem.

hiredman21:06:33

the bindings in for are names that map to values in the environment

hiredman21:06:48

so do that, but not in the environment, use a map

hiredman21:06:54

so one reason you may want some kind of runtime associative structure is the name and the value come in as an argument that get passed on to something else

hiredman21:06:56

user=> (for [{:keys [name value]} [{:name "a" :value 1}]] {name value})
({"a" 1})
user=> 

hiredman21:06:11

the map can be passed on to another function or whatever in the body of the for

ibarrick21:06:36

So how would this apply to a case where I want the behavior of:

(for [x [1 2 3] y [4 5 6]] [x y])
or:
(for [x [1 2 3] y [3 6 7] z [0 9 6]] [x y z])
But I don't know until runtime how many arrays will need to be "for"ed over? The maps code you posted looks like I'm just destructuring something I already know the format of.

hiredman21:06:14

go higher order, instead of operating on arrays, operate on a collection of arrays

hiredman21:06:38

that is just (map vector collection-of-arrays)

hiredman21:06:29

well, not entirely, of course, because for is a cartisian product, but something like that

hiredman21:06:21

you can also do what I usually call a recursive for, which comes are fairly often (for me)

hiredman21:06:02

something like (fn this-fn [some-stuff] (for [a some-stuff i (this-fn some-stuff)] i))

ibarrick21:06:15

Just found this online, looks like exactly what you were saying:

(defn cart [colls]
  (if (empty? colls)
    '(())
    (for [x (first colls)
          more (cart (rest colls))]
      (cons x more))))

ibarrick21:06:54

It would have taken me a long time to come up with that lol

ibarrick21:06:52

Do you know of a book that goes over patterns like that? or is it just something that comes with time?

hiredman21:06:02

I dunno, I use for a lot

hiredman21:06:05

I think the best way to think about for is in is the SELECT statement for clojure data

hiredman21:06:45

but that only helps if you are kind of familiar with subselects that kind of weirder sql stuff

bcbradley23:06:38

hey guys, is there any such thing as a folding transducer?

hiredman23:06:10

a transducer is a function that takes a stepping function and returns a stepping function

bcbradley23:06:35

i'm pretty comfortable with the idea of a transducer

Alex Miller (Clojure team)23:06:58

what do you want to do?

hiredman23:06:16

so given a transducer is a function from stepping function to stepping function, where does folding fit in to that?

bcbradley23:06:30

reduce will take collection and "divide and conquer" on it. It will split up the collection into subcollections and apply reduce to each part.

hiredman23:06:31

(or where would it?)

Alex Miller (Clojure team)23:06:55

oh, you mean like fold as in clojure.core.reducers?

bcbradley23:06:07

i'd like a transcducible process

bcbradley23:06:17

for example "(transduce ...)" is such a process

bcbradley23:06:23

so is "(educe ...)"

bcbradley23:06:30

i want one that folds

hiredman23:06:41

in so much as fold can take a step function, you can use transducers to produce that step function

Alex Miller (Clojure team)23:06:44

there is no parallel transducible process (yet)

Alex Miller (Clojure team)23:06:53

partitions in fold are reduced so you can use transducers on each partition of a fold

Alex Miller (Clojure team)23:06:03

there are some examples of that floating around

bcbradley23:06:25

i was just curious about it

bcbradley23:06:45

its not like my life depends on it, so I guess the most pragmatic thing would be to wait for it to enter the standard (you said "yet")

bcbradley23:06:14

i'm looking at the addition of "spec" to 1.9

hiredman23:06:17

a transducer is a function that takes a step function and returns a step function, so you can use transducers to build such functions and use them anywhere such a function can be used

Alex Miller (Clojure team)23:06:31

it's something we've talked about and I know Rich has ideas about it

Alex Miller (Clojure team)23:06:43

not currently something we're working on for 1.9

bcbradley23:06:49

i'm wondering if spec could be used to make guarantees on data about associativity and commutativity

Alex Miller (Clojure team)23:06:59

that's an interesting idea

bcbradley23:06:05

and if so, a transcuder could just do it all under the hood

Alex Miller (Clojure team)23:06:06

I don't think we would (for performance reasons)

Alex Miller (Clojure team)23:06:25

but it's interesting :)

bcbradley23:06:59

oh well, i have to say clojure is light years ahead of anything i've seen in any other language

bcbradley23:06:07

the fact that it still has so much room to grow is astounding

bcbradley23:06:14

lisp is paradise xd

Alex Miller (Clojure team)23:06:56

personally, I'd put a higher priority on primitive support in transducers than fold - that's much harder to do outside the core than fold (see Tesser for example)

bcbradley23:06:32

i guess you wouldn't really have to use "spec" to assure commutativity, you could just offer a protocol that ensures that the order of the arguments for an operator are arbitrary

bcbradley23:06:52

for a function with two arguments, thats not a big deal

bcbradley23:06:56

but what about 3, 4, or N?

hiredman23:06:08

you want a set of arguments

bcbradley23:06:15

right, exactly

bcbradley23:06:41

another way to say "commutative function" is to say "the arguments of the function are a set, not a vector"

bcbradley23:06:56

you could say (fn #[a b c] (...))

bcbradley23:06:10

well you know what i meant

hiredman23:06:32

or just write a function that takes a set as an argument

bcbradley23:06:50

yes, but i'm thinking of it from clojure's point of view

bcbradley23:06:06

a function that takes a single argument doesn't tell clojure much of anything

bcbradley23:06:10

even if that argument happens to be a set

bcbradley23:06:20

however, a function who's argument collection IS a set

bcbradley23:06:34

tells clojure that the order of the arguments is effectively arbitrary

bcbradley23:06:38

in other words, that it is commutative

bcbradley23:06:11

that only leaves associativity

bcbradley23:06:25

how would you tell clojure about associativity in an elegant manner?

bcbradley23:06:13

i guess you could do it with a macro like (associative (...) (...) (...) (...) (...))

bcbradley23:06:25

that doesn't feel very elegant to me though

bcbradley23:06:13

normally functions like map and filter and such will return a transducer if you don't supply a collection

bcbradley23:06:28

what you want in that case is to say something about the associativity of the transducers you have

bcbradley23:06:41

and that obviously depends on the calculations the transducers each do

bcbradley23:06:58

you can make transducers out of other transducers

bcbradley23:06:15

perhaps you should be able to make an "assoc-transducer" out of transducers

bcbradley23:06:40

but then, if the order of composition does not matter...

bcbradley23:06:54

isn't that the same thing as commutativity on composition?

bcbradley23:06:01

isn't the definition of associativity (fn #{a b c} (comp (a b c)))

bcbradley23:06:09

er (comp a b c)

bcbradley23:06:12

srry old habit

bcbradley23:06:18

i think adding support to clojure for SETS of arguments, rather than just ordered sets (vectors) of arguments, would enable a quick and idiomatic solution to the problem of representing associativity and commutativity for a variety of applications, like "folding" transducers and such

bcbradley23:06:26

and it wouldn't be noisy or get in the way