Fork me on GitHub
#clojure-dev
<
2018-03-30
>
rickmoynihan10:03:58

Is there a reason apply can’t apply 0-arity functions? e.g. I’d expect (apply println) to work. Which would be useful sometimes e.g. (map apply [println ,,,]) and many other cases.

rickmoynihan10:03:53

(map apply [println]) seems more expressive (and general) than (map #(%) [println])

bronsa10:03:20

no technical reason

rickmoynihan10:03:37

I’m surprised that I can’t seem to find a JIRA for this

bronsa10:03:42

create one!

rickmoynihan10:03:11

Yeah I will… just checking there isn’t one already

bronsa10:03:24

I'm not aware of any

rickmoynihan10:03:50

just renamed ticket “apply should support application without having to supply args”

rickmoynihan10:03:28

as previous name “apply should support 0-arity functions” was technically wrong… as (apply println []) works

bronsa10:03:30

one other related thing I've often missed is the lack of an invoke function

bronsa10:03:53

with (invoke f x y ..) = (f x y ..)

bronsa10:03:10

doesn't seem so useful at first but there's a surpising number of cases where it would be

reborg13:03:38

#(%1 %2) ? Or do you dislike the admittedly contrived syntax?

bronsa13:03:12

yeah that's what I end up doing, but it's not polyadic and I'd rather a named function

reborg13:03:24

fair enough, we have things like identity or constantly that are not that far away

bronsa10:03:40

condp is one example

rickmoynihan10:03:06

hmm that seems familiar, but I’m struggling to see the utility… can you explain more?

rickmoynihan10:03:05

I’m sure I’ve run into the need for it in the past, but can’t for the life of me remember when or why

bronsa10:03:59

stupid example

bronsa10:03:03

(defn invoke [f x] (f x))

(defn classify [x]
  (condp invoke x
    pos? :pos
    neg? :neg
    zero? :zero))

bronsa10:03:43

or I've even had cases where I've wanted to do something like (apply map invoke args)

bronsa10:03:58

where args is [fs & vals]

rickmoynihan10:03:53

yeah I think the later is closer to the cases I’ve seen this

bronsa10:03:13

the former is quite common when you write code that does predicate dispatch

rickmoynihan10:03:33

but the condp example would be cute if invoke existed

bronsa10:03:09

oh well, now I'll make a ticket :)

rickmoynihan10:03:06

though it’s perhaps not sufficiently common to merit being in core

bronsa10:03:22

it's common enough in the clojure I write

schmee10:03:29

while we're on a ticketing spree, has there been any discussion around allowing underscore in numerical literals in clorjure, ie (def a 1_000_000)?

bronsa10:03:50

I remember somebody asking about it around 2010 in irc, when I first joined the community

bronsa10:03:08

I assume rich doesn't like the idea if it hasn't happened since but who knows

rickmoynihan10:03:14

@schmee: not sure I like the idea of underscored numerics, as it would presumably have implications for EDN etc too

schmee10:03:02

hard to object to that unless you're more explicit about what those implications are 😉

rickmoynihan10:03:14

well I think if the above was supported there might be an expectation on users that EDN should probably support it too, but EDN has many implementations in many languages so adding it to EDN would be a breaking change. Admittedly there’s plenty of clojure syntax not supported in EDN, but this would increase the syntax gap further.

rickmoynihan10:03:38

plus perhaps the mixing of _ and -’s in clojure syntax is perhaps aesthetically displeasing

rickmoynihan10:03:34

and you already have the 1e6 syntax

schmee11:03:33

1e6 doesn't help for things that aren't powers of ten

sundarj18:03:18

you can do things like 1.2e2, fyi

schmee11:03:11

I see your point about EDN but like you say there is already lots of Clojure syntax, and I don't think it should be a requirement to extend EDN to extend Clojure

rickmoynihan11:03:17

agreed, but how much syntax for numbers do we need?

schmee11:03:00

it's not a crucial thing for sure, my motivation is that in Java I can write long l = 1_234_567;, and since Clojure uses long literals, it would be nice if it worked there as well 🙂

rickmoynihan11:03:02

I don’t think that is a requirement… but the differences are a barrier for developers… e.g. it’s another thing where copy/pasting between the two will fail

rickmoynihan11:03:02

yeah it’s cute and handy for eyeballing long numbers Just pointing out some arguments for why it’s not necessarily good to do. I’m not sure the benefits are worth the cost.

rickmoynihan11:03:59

but I’m not clojure core so it’s not me you need to convince 🙂

rickmoynihan11:03:25

While we’re on the subject of missing functions @bronsa it’s also frustrating that we have vec vector and set hash-set constructors, but for hashmaps you only get one hash-map and have to (apply hash-map ,,,,), not sure what you’d call it though, as obviously map is taken.

bronsa11:03:28

meh, into {}

rickmoynihan11:03:37

yeah was going to say at least we have into

bronsa11:03:31

i've sometimes wished vec set and the non existent map* would've been called ->vec ->set ->map

bronsa11:03:45

but way too late for that

rickmoynihan11:03:52

yeah that would’ve been better

Alex Miller (Clojure team)12:03:33

There’s no reason we haven’t added the underscore number literal syntax - generally for the primitives we match Java so that would be in line. I think if you are only accepting it as a reader syntax and never printing it, it’s a smaller difference than edn, which could either get it later or not at all

schmee12:03:47

I see! I might try adding it and open a ticket if I can get it working

Alex Miller (Clojure team)13:03:13

Just a regex change in LispReader

gfredericks14:03:52

@rickmoynihan @bronsa I'm pretty sure invoke is exactly what @rickmoynihan was asking for originally; seems to fit better that having a variant of apply

gfredericks14:03:29

oh I see alex already commented on the ticket

rickmoynihan16:03:21

yeah the reasoning makes sense. invoke is a better name for what I want

bronsa16:03:14

@rickmoynihan maybe leave a comment on the invoke ticket with your use case then

bronsa16:03:53

if you have real world examples that usually helps getting a ticket considered

schmee16:03:06

also, press the "Vote" button :thumbsup:

rickmoynihan16:03:23

have just done that 🙂

rickmoynihan16:03:31

My use case is basically I have functions that have had their arguments partially applied and they just need to be called. e.g. things like (map (comp :result invoke) fns)

rickmoynihan16:03:42

or variants of

dominicm16:03:28

I had a use-case for invoke. (extend clojure.lang.Keyword Getter {:get invoke}). But that's the first time in 2 years I think :thinking_face:

notbad 4
ghadi16:03:20

this seems niche / clever

slipset17:03:05

Had a usecase where I think invoke would have made my code a bit clearer:

slipset17:03:08

(def fizzbuzzes
  (let [num str
        fizz (constantly "fizz")
        buzz (constantly "buzz")
        fizzbuzz (constantly "fizzbuzz")
        fns [num num fizz
             num buzz fizz
             num num fizz
             buzz num fizz
            num num fizzbuzz]]
         (map apply (cycle fns)
                     (map (comp vector inc) (range)))))

slipset17:03:08

I have a feeling that using invoke instead of apply, I could have gotten rid of the (comp vector inc) and only used inc.

schmee17:03:24

I'm poking around with TryExpr in the compiler (patches incoming! 🙂 ) and I'm trying to understand this part: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Compiler.java#L2277-L2278

schmee17:03:02

I see that it wraps the try in a fn, but what I don't see is why this is necessary and what it accomplishes

schmee17:03:25

any pointers in the right direction are much appreciated

hiredman17:03:28

it is an easy way to preserve expression semantics

schmee17:03:26

hmm... so what are the semantics that need to be preserved?

hiredman17:03:26

(try ...) returns a value

hiredman17:03:01

the bytecode for try/catch/finally on the jvm looks very different from that, and requires you to preserve stack height invariants (if I recall)

hiredman17:03:33

and doing all that bookkeeping inside nested contexts in a single method is a pain, when you hoist the try out as fn like that, it is compiled in its own method, reducing it to an easier problem

hiredman17:03:03

a lot of more complex expressions in the compiled get similar treatment when not in the return context

schmee17:03:42

ahh! so what is different about the return context that makes it not require this special treatment?

hiredman17:03:11

the return context is sort of like being a tail call, you don't need to keep track of things to clean up afterwards, you can just return the result

schmee17:03:59

nice, thanks for the explanation! :thumbsup:

schmee17:03:50

so the value of context also controls whether you're allowed to recur or not?

hiredman17:03:17

I have an unsuccessful patch that I haven't worked on in a year or two which replaces hoisting as once fns with hoisting to static methods

hiredman17:03:47

I am not sure, I don't remember if C.RETURN is used for that

hiredman17:03:48

loops are another kind of form that is hoisted out if not in the return context

schmee17:03:00

hah, I was just looking at that patch earlier today! 🙂

hiredman17:03:40

if you are interested in clojure compilers, the core.async go macro is basically a compiler where the src and target language are both clojure (or clojurescript) an it is written in clojure instead of java

bronsa18:03:13

C.RETURN/EXPR vs C.STATEMENT is essentially about stack handling -- to figure out when a value needs to be discarded or a nil needs to pushed onto the stack

bronsa18:03:25

RETURN vs EXPR is about tail position

bronsa18:03:33

C.EVAL you don't want to know

trollface 4
schmee18:03:39

and the reason you have to do the stack bookkeeping is because the bytecode verifier will complain otherwise?

bronsa18:03:08

that's part of it

bronsa18:03:43

you don't want to have different stack depths in different branches, that will mess up things horibly (and the verifier will also complain in some cases)

bronsa18:03:59

but also you don't want a function returning void to be in return position and not push nil onto the stack

bronsa18:03:50

there's a few more subtleties but this is an ok mental model of how this works

schmee18:03:50

I see, so basically it comes down to the gory details of the JVM spec

schmee18:03:23

come to think of it, is there even anything in Clojure that is not an expression?

schmee18:03:53

is the distinction between the statements and expressions a compiler thing or is it visible in the language?

bronsa18:03:11

(monitor-enter x) is not an expression for example

bronsa18:03:25

if you really want to be picky

hiredman18:03:03

in clojure everything is an expression, the jvm is stack machine so the expression/statement different isn't exactly the same, but expressions are roughly instructions that push data on to the stack, and statements are instructions that don't push data on to the stack

hiredman18:03:57

methods that return void don't push null on to the stack, they push nothing, so clojure has make up the difference and push a nil