Fork me on GitHub
#beginners
<
2017-06-01
>
lmergen13:06:38

there is some java API i’m using that wants a byte[] — I’m using (.getBytes mystring), which the REPL tells me is a [B — is this the same type ?

lmergen13:06:51

if so, is count the most reliable way to get the total length of the byte array ?

donaldball14:06:15

Yes, that is the same type, see (type (byte-array 1)). count is the polymorphic way of counting collections in clojure. You might see a minor speed improvement by using alength which is specific to arrays.

john20:06:43

nvtf: One solution, don't bother with the macro:

(defn mk-printer [pattern]
  (fn [input] 
    (if (:color input) 
      (prn (str (:color input) "-colored " (get input pattern))) 
      (prn (get input pattern)))))

(def print-lemons (mk-printer :lemons))

(print-lemons {:lemons "lemons" :color "red" :not-printed "void"})
;cljs.user=> "red-colored lemons"
Then maybe sprinkle macros on top.

john20:06:10

But it's nice having the plain functional version around, in case you need the flexibility.

nvtf20:06:56

@john the thing is that I want to be able to do something like (let [color "black"] (defprinter ...)) or the pattern can even be [to-print color], doesn`t necessarily need to be on a dict

john20:06:36

Yeah, reusing the destructuring machinery is def a neat trick.

nvtf20:06:42

and I'm more interested on the why it doesn't work rather than the best solution tbh. mainly: 1) eval doesn't seem to capture local scope. is there a way to do that? 2) is there a way to write a macro that: tries writing some code and in case that doesn't compile tries to write another? how or why not?

nvtf20:06:54

(thanks for the answer though :) )

john20:06:34

You can grab the local scope in clojure, but working with outer scoped variables in ClojureScript is non-trivial.

john20:06:44

As such, I haven't explored 1 much. I believe try/catch should work while compiling macros, but we do have some macro wizards around here, so hopefully someone will know the answer.

noisesmith20:06:17

@nvtf clojure would be a much messier language if eval had implicit access to locals, and we indulged in writing code that exploits that. This is a pretty basic element of clojure’s design and relates to problems that came up constantly in common lisp code

noisesmith20:06:54

if there are values a macro should use, pass them as args to the macro

noisesmith20:06:57

also your code would work if you got rid of the eval - color would capture a local binding of color - but it’s still not a good way to do macros

john20:06:33

(let [color "red'] (defn yata... doesn't look idiomatic either, IMO

john20:06:10

@noisesmith isn't it more idiomatic to simply rebind some *global* in some local let binding, in those cases when you want to grab from outer scope? Within a function?

nvtf20:06:57

>also your code would work if you got rid of the eval - color would capture a local binding of color - but it’s still not a good way to do macros only if color is present in the pattern, otherwise it's a compile error. and it feels like there should be a way to recover from that, which I couldn't find 🤔

noisesmith20:06:15

implicit access to globals is a thing, of course, that’s what “global” means. But we don’t do implicit access to non-global parent scopes, and that’s on purpose, and it’s a good thing.

noisesmith20:06:48

@nvtf well, the patterns is data when your macro runs, you can check what’s in it

noisesmith20:06:05

(a form, but data if it’s a literal…)

noisesmith20:06:17

and you can emit code that does an explicit lookup in the data object

noisesmith20:06:24

(with a sensible default)

nvtf20:06:26

yep, thought about that, but that doesn't solve me wanting to do (let [color "black"] (defmacro ..)

noisesmith20:06:45

@nvtf right, and what I’m saying is that code like that is terrible

dpsutton20:06:13

it's very scheme-ish

noisesmith20:06:14

if the macro should know about color, pass it in or make it global

nvtf20:06:39

I don't care about how terrible it is and I don't intend to actually use it, it's more a question of "can I? if yes, how? if no, why not?" to understand the language better :P

noisesmith20:06:54

I’m doing my best to explain why

john20:06:54

(let [color "black"] (defmacro ..) or (let [color "black"] (defn ..)?

noisesmith20:06:12

upword scope reach leads to weird and hard to understand bugs

nvtf20:06:41

so that is not possible in clojure by design?

noisesmith20:06:52

not without some serious hacks

noisesmith20:06:56

right, it’s by design

noisesmith20:06:23

it’s a simpler hack (but still a hack) if you get rid of the eval

nvtf20:06:14

> some serious hacks that's pretty much what I want/can't come up with one/it's bothering me haha

noisesmith20:06:41

there’s an infinitude of ways to do it, none of them leverage clojure’s actual intended programming features

noisesmith20:06:11

for example macros have an implicit &env arg, 90% of the things you do to it will just make everything crash, but it’s possible to use it to access locals.

nvtf20:06:19

yeah, that pretty much solves it, thanks =)

nvtf20:06:29

didnt know about that

john20:06:17

A function at runtime could check to see if color is defined and branch on that condition.

noisesmith20:06:04

@nvtf as long as we are clear that there are very good reasons that we don’t write code this way

dpsutton20:06:31

except for the cider debugger ...

mobileink20:06:34

@ghadi, @noisesmith to pick a nit: what is a lazy data structure? to me, laziness always and only refers to evaluation. maybe "clojure's lazy evaluation is not global, but restricted to the evaluation of certain data forms" or sth like that?

noisesmith20:06:05

a lazy data structure avoids computing data that isn’t accessed

ghadi20:06:06

there is no lazy evaluation in clojure.

ghadi20:06:14

Only strict.

ghadi20:06:45

A lazy sequence usually looks like <VALUE, Thunk() >

ghadi20:06:00

if you call first on it, you get VALUE, if you call next on it, it will realize the thunk

john20:06:11

But is it fair to say that the datastructure is controlling the evaluation?

mobileink20:06:33

i don't see how that could be true. obviously we cannot strictly eval an infinite list, right?

noisesmith21:06:32

sure - just don’t access contents - you just get the list back

noisesmith21:06:49

it doesn’t compute elements until you access

mobileink21:06:06

@noisesmith that can only work if part of the data is represented as a fn. which must be lazily evaluated. no?

noisesmith21:06:24

no - it’s not called until you call next on it’s parent

noisesmith21:06:49

that’s not lazy evaluation, it’s just a funny definition of “next” that includes function calls

noisesmith21:06:53

(and caching)

mobileink21:06:06

isn't that lazy (aka JIT) evaluation?

noisesmith21:06:17

no more than any other function call is?

noisesmith21:06:47

I mean it kind of cheats by hiding a function call inside a supposed data accessor

noisesmith21:06:57

but that’s not the same as lazy evaluation as a language semantic

mobileink21:06:13

in lambda terms, you don't reduce until you need to.

noisesmith21:06:40

I think this makes much more sense (and you get a more accurate view) if you treat this as an OO thing

noisesmith21:06:58

the .next method of the data structure object is a memoized function

mobileink21:06:23

ah, i don't think of evaluation strategy as semantic. it does not affect the meaning of a program.

ghadi21:06:16

thinking in terms of Haskell evaluation will not help here. Lazy seqs just delay computation of the rest part of the seq

john21:06:42

depends on your meaning of meaning 😉

mobileink21:06:25

it does not affect the denotatuon of the program text afaik.

dpsutton21:06:12

doesn't that require purity to be true?

mobileink21:06:26

@ghadi isn't that lazy eval, by definition? what am i missing?

dpsutton21:06:27

or referential transparency

john21:06:56

It's almost like each new link in the list is wrapped in a deref. Therefore realizing the whole sequence requires the repeated dereferencing of each tail.

john21:06:22

map and others just defer the dereferencing to further up the chain.

mobileink21:06:50

@dpsutton i don't see why. couldn't one implement inifinite data in a java program?

dpsutton21:06:21

yup. in fact clojure does just this

dpsutton21:06:00

> thinking in terms of Haskell evaluation will not help here > it does not affect the denotatuon of the program text afaik. > doesn't that require purity to be true?

mobileink21:06:14

Y combinator?

ghadi21:06:17

the term "lazy evaluation" is causing confusion -- i'm not going to use it anymore. Clojure uses strict/eager evaluation, like Javascript, Java, Ruby... Clojure exposes a mechanism for delayed realization of sequences.

mobileink21:06:50

to me it looks like clojure implements lazy eval as a language feature, but only in a restricted form.

ghadi21:06:30

whatever works for you

mobileink21:06:01

i guess i don't see why we should call the kind of eval needed for lazy seqs strict. seems contradictory to me.

dpsutton21:06:41

because the first thing application does is look at all of its arguments

mobileink21:06:17

what is the difference between delayed eval and lazy eval?

seancorfield21:06:21

(delay (reduce f ...)) vs (map f ...)

dpsutton21:06:38

so (fn 2 3 (+ 3 4) lazy-seq) evalues 2, evaluates 3, evaluates (+ 3 4) and evaluates lazy-seq which turns out to be an object implementing public final class LazySeq extends Obj implements ISeq, Sequential, List, IPending, IHashEq{

mobileink21:06:43

no. the first thing application does is determined by the eval strategy. in tools like coq you can explicitly set this.

mobileink21:06:31

@seancorfield haha. and how are those forms evaluated?

dpsutton21:06:00

delay is a macro so it's different

dpsutton21:06:48

but it just turns its list argument into (list 'new 'clojure.lang.Delay (list* ^{:once true} fn* [] body))`

dpsutton21:06:08

but this explains the point

dpsutton21:06:38

it's essentially (fn* [] (reduce f ...))) so notice that it's lazy and immediately evaled, it's just put into a thunk

mobileink21:06:06

delayed eval just is lazy eval, afaics. the question is whether there is language-level support for it.

dpsutton21:06:27

this new clojure.lang.Delay object is created and allocated

john21:06:10

The "main execution thread" is not lazily evaluated. Perhaps that's the distinction.

mobileink21:06:06

evaled and put into a thunk. isn't that always how lazy eval works in languages that support it?

john21:06:10

By default, evaluation is eager, but for a particular kind of data structure.

mobileink21:06:40

@dpsutton i'm not talking about Delay or any other fn in clojure but about computation.

mobileink21:06:15

@john exactly my point. it seems misleading and confusing to say that clojure eval is always and only strict.

ghadi21:06:24

I think we're beating a dead horse here. Can we move this to a thread pls?

mobileink21:06:49

moved to #off-topic

vitruvia23:06:30

What works like merge and conj for hash-maps but keeps the left element as the first element of the seq? Like (function {:a 1} {:b 2}) ;=> {:a 1 :b 2}

john23:06:47

Maps aren't supposed to be treated like they have an order. You could make a custom sorted map with a comparator.

john23:06:10

But then you would need to ensure the left most object parameter is correct with respect to the ordering, being less-than the parameter on the right.

john23:06:12

Or, did you not mean the left most element in the map?

vitruvia23:06:17

hm I don't know if this would work for my function

(defn my-frequencies-helper [freqs a-seq]
  (if (empty? a-seq)
    freqs
    (let [el (first a-seq)
          el-freq (merge freqs {el (count-elem el a-seq)})
          new-seq (remove #(= el %) a-seq)]
      (my-frequencies-helper el-freq new-seq))))


(defn my-frequencies [a-seq]
  (my-frequencies-helper {} a-seq))

noisesmith23:06:18

there’s at least one lib that has a hash-map that preserves insertion order

noisesmith23:06:29

but you probably don’t need it

john23:06:12

where's the need for the ordering?

vitruvia23:06:17

@john I'm not sure if I understand your question. My current function (posted above) returns (my-frequencies [:a "moi" :a "moi" "moi" :a 1]) ;=> {1 1 "moi"3 :a 3} but I wanted it to return according to how the elements show in the original sequence, for example {:a 3 "moi" 3 1 1}

john23:06:02

Just because?

vitruvia23:06:29

That is how the exercise asks me to return. I don't know if the tests will try for that though

noisesmith23:06:39

whose exercise?

john23:06:18

Maybe they want a sorted map??? indexed off the insertion order?

vitruvia23:06:52

@noisesmith http://iloveponies.github.io/120-hour-epic-sax-marathon/recursion.html a course from helsinki university, I had asked for opinions before if you remember.

noisesmith23:06:05

OK - because equality for maps in clojure ignores order

noisesmith23:06:36

if it’s meant to be a clojure task, your code will pass regardless of order

vitruvia23:06:03

I guess I won't worry about order, then

john23:06:14

I was so confused

vitruvia23:06:16

thanks both of you xD

john23:06:26

The intuitions we take for granted...

vitruvia23:06:33

sorry for the confusion lol