Fork me on GitHub
#clojure
<
2020-02-09
>
vemv05:02:56

I recall some ANSI-colored string screwing my emacs repl in the past (I would see escape codes instead of something legible), however now I'm using something that ansi-colors output and it looks fine. That makes me wonder if is there a 'good' and 'bad' way to color strings? For a tool of my own I wouldn't want to emit output that can look broken for certain users

seancorfield05:02:51

That's why most tooling that does output coloring has an option to turn it off I guess.

👍 4
seancorfield05:02:27

Maybe CIDER was updated to allow ANSI-colored output since you had that problem?

vemv05:02:13

I couldn't tell :) mix of blurry memories and custom emacs-isms But yes, an option can be the simplest approach

slipset08:02:55

I believe that most programs like ls and friends check if output is a pipe or std-out and if the former, turns off coloring. I might be dreaming, though

dominicm11:02:33

Jansi has a way to probe the environment for ansi support, although it does not detect cider as such an environment.

👀 8
hindol12:02:14

@slipset Yes, that's exactly how it works. Otherwise it would be way too inconvenient turning colors on and off with command line args. The way coloring works is by emplacing color codes in the output string itself. The next program down the line removes this color information and draws those parts with the appropriate color.

Jakob Riishede Møller15:02:35

Good afternoon/evening/morning? I want to follow the book Clojure for Machine Learning (https://www.amazon.com/dp/1783284358/ref=cm_sw_r_cp_ep_dp_CJbkAbX33TGVE) In Chapter 1 about linear algebra (matrices), the clatrix library is used motivated by better performance but that library allows mutable matrices, defined by using def. I wonder whether there are some general guidelines for programming in Clojure, when working with mutable models?

Jakob Riishede Møller15:02:08

I guess the question applies to the functional programming paradigm in general?

p-himik15:02:51

AFAICT, the general paradigm is "don't work with mutable data structures unless you really have to". E.g. when the performance is really important.

👍 4
Jakob Riishede Møller16:02:25

OK - let's imagine it is 😉

Jakob Riishede Møller16:02:14

(I think most image libraries do not copy entire images all the time - to provide another example)

p-himik16:02:34

For example, even Pandas and Numpy offer their functions in two ways - one that mutates the data in-pace, and the other that creates a changed version of the data. Well, then you have to find the bottlenecks and restrict mutable data structure usage to those particular places. Everything else - immutable.

vemv16:02:01

Worth pointing out that clojure.core has quite a wealth of array-related functions: from basic stuff to amap, areduce... Within reason, one can try preserving clojure's model even when working with mutable things. I wonder if transducers work with arrays? Can't immediately tell myself.

Jakob Riishede Møller16:02:09

OK - but I was thinking whether there are any patterns for programming within those bottleneck areas

p-himik16:02:25

But it also depends on what you're doing I guess. If all you want to do is apply some linear non-concurrent pipeline where you don't share any data at all, then immutable data structures don't give you that much and you should be fine with something mutable.

Jakob Riishede Møller16:02:17

Good point - I am also having an intuition about somethin strictly sequential

p-himik16:02:35

The only pattern I've seen is the one I have already described. Extract and confine all mutations under some API that doesn't mutate anything by itself. Similar to how transients are used in Clojure.

jsn18:02:04

So, in deftype I can have primitive fields, or mutable fields, but not both (primitive mutable fields)?

jsn18:02:32

I'm getting an impressively cryptic error:

#error {
 :cause Bad type on operand stack
Exception Details:
  Location:
    dtype/VM.run()Ljava/lang/Object; @9: putfield
  Reason:
    Type long_2nd (current frame, stack[2]) is not assignable to integer

andy.fingerhut18:02:56

I do not know what you have tried, but Clojure/Java allows deftype with mutable primitive fields. Here is an example from the core.rrb-vector contrib library: https://github.com/clojure/core.rrb-vector/blob/master/src/main/clojure/clojure/core/rrb_vector/rrbt.clj#L79-L80

jsn18:02:25

yes, it's java Clojure; it seems to happen to some of the primitives, int and byte cause that, long doesn't

andy.fingerhut19:02:04

I do not know what the difference between your use and core.rrb-vector's might be, but core.rrb-vector's use does not cause such an error.

jsn19:02:18

float causes that, double doesn't. Apparently it has to do with the bitsize of the type. Short types get the shorted, huh

andy.fingerhut19:02:22

and has a couple of mutable int fields

jsn19:02:13

ah, I see

jsn19:02:34

(set! intfield 1) causes that

jsn19:02:58

(set! intfield (int 1)) doesn't

jsn19:02:07

Still looks like a bug to me (at the bare minimum, the error message could be less arcane), but yeah, I can work with that

andy.fingerhut19:02:16

You can file an issue on http://ask.clojure.org if you consider it a bug, and see what the maintainers think. Precise reproducible code, version of Clojure used, error message, all useful in a report.

markmarkmark19:02:04

isn't the issue that Clojure uses longs and doubles? but you can't assign longs to ints and doubles to floats because of the possible loss of data.

jsn19:02:23

you can assign 1 to e.g. int array location or pass it to an int-typed loop var just fine

markmarkmark20:02:56

After looking into this, I think the issue is that the other examples such as assigining to an array and whatnot, are methods that are subject to the Reflector which massages values to be what they need to be. But, set! is a special form that just spits out bytecode without trying to make things fit. so all it sees is that you're trying to emit bytecode that puts a long into an int field.

andy.fingerhut20:02:11

I am not sure exactly where the source of that error is -- perhaps the asm library that the Clojure compiler uses to generate Java byte code? If so, it is at a fairly low level that the issue is being caught and reported right now.

andy.fingerhut20:02:15

It is also for a fairly low level use case within Clojure that a lot of people don't ever reach for. Not everyone uses deftype, and among those who do, only some use mutable fields, and only some use fields with Java primitive types.

andy.fingerhut20:02:48

I agree that all other things being equal, it would be nice to have descriptive clear error messages that you can understand quickly the first time you see them. The cost of implementing such a clear error message for this kind of code may be fairly high relative to the number of people who would see it.

markmarkmark20:02:30

I guess one thing to clarify: that error is given by Java. Clojure happily writes the Class file for the type with the mismatched types.

markmarkmark20:02:45

so I guess it would be up to the bytecode emitter for set! to maybe do some checking.

jsn18:02:08

hmm, it happens for int , but not for long !

vemv19:02:09

Played a bit with this, out of curiosity. The following fails to compile:

(deftype A [^:unsynchronized-mutable ^long x]
  P
  (some-method [_]
    (set! x (int 2))
    (println x)))
...Because the type hint says long, but the method call says int. i.e. watch out for such inconsistencies. Anyway what does your code look like?

jsn19:02:47

(defprotocol IVirtualMachine (run [code]))

(deftype VM [^int ^:unsynchronized-mutable pc]
  IVirtualMachine
  (run [code]
    (set! pc 1))
  )

vemv19:02:10

1 is a Long in clj . You can check it with (class 1) Assigning longs to an int "slot" is inherently dangerous, since a long can always represent a bigger value than it is representable as an int So it's a good thing that you need an explicit (set! pc (int 1))

jsn19:02:56

It's inconsistent (you can assign 1 to an int array location or pass it to a int-typed loop var without any issues whatsoever)

jsn19:02:14

you can even pass longs to unchecked-add-int , and it just silently does the right thing

vemv19:02:29

deftype aims to emit efficient code, so it wouldn't surprise me if it imposed greater constraints than those encountered in most other parts of the Clojure language

vemv19:02:54

unchecked-add-int, clojure.core/+ etc aren't extremely performance-oriented (AIUI: they balance performance with 'forgiveness'), which is the reason people may feel compelled to author e.g. https://github.com/ztellman/primitive-math for having more deterministic performance expectations

jsn21:02:54

Here's another useless piece of micro-optimization trivia: https://gist.github.com/jsn/cfbf2e51393c4cba062898e452f0d23c ; trampoline seems very fast until it's used for more than one function, and then it gets very slow.