Fork me on GitHub
#beginners
<
2018-11-13
>
jaawerth01:11:17

@jpsoares106 4clojure is nice for doing various exercises but for some more curated exercises with, in turn, more curated solutions, I'd recommend https://github.com/gigasquid/wonderland-clojure-katas

jaawerth01:11:11

@jpsoares106 If you're interested in something more introductory, when I was first learning I started with https://github.com/functional-koans/clojure-koans before moving on to those katas. I don't remember seeing viewable solutions for the koans, but it's set up so you can easily run through them iteratively, with all the tests already set up (whereas the katas have links to solutions you can view, and the first one or two have tests written for you, the later katas require you to also write the tests)

minhnn09:11:33

Hi, I am new to clojure

minhnn09:11:52

I am using Lighttable, It's easy to me

minhnn09:11:24

But how can I interact with lein repl(2.8.1)

andy.fingerhut09:11:29

I have not used Lighttable before, so can't help you there. What kind of system are you using? (e.g. Windows, Mac, Linux?)

minhnn09:11:11

I'm using window

andy.fingerhut09:11:12

Does Lighttable have a Clojure REPL built in? If so, is it not doing what you want, and that is why you want to run lein repl?

andy.fingerhut09:11:41

It should certainly be possible to run a lein repl separately from Lighttable, too, if you wanted to, from a Windows command line.

minhnn09:11:42

I want to get input from keyboard such as (read)

andy.fingerhut09:11:15

I will probably not be awake much longer here, but wanted to mention that there is a #lighttable channel, too, but you are certainly OK to ask here as a beginner. I do not know how many people are in the #lighttable channel. I am not trying to scare you off, but do realize that because Lighttable development stopped a year or two ago, it may not be the most often used development environment for Clojure. It does have some nifty features, that is certain.

Sturm12:11:49

how do I check if a vector of booleans are all true? (apply 'and [true false true]) doesn't seem to work

didibus04:11:13

If you want true as in exactly equal to true, use (every? true? [...]). But if you want truthy, as in true if everything is not nil or false, you can use (every? identity [...])

dpsutton12:11:22

Look at the higher order functions every, some, every-pred, and some-fn

dpsutton12:11:02

They are very handy. Do you know why you can't apply and?

noisesmith16:11:22

Even worse, 'and in that example isn't a macro, it's a function that attempts to look up a symbol and optionally return a provided default if not found

noisesmith16:11:59

For the right input it even looks like it works ('and true true)

Alex Miller (Clojure team)16:11:41

(every? identity [true false true]) is probably the best answer here

Lennart Buit19:11:42

So I was looking at this because I had implemented all-true? and any-true? using reduce before. This every trick is obviously a way to implement all-true?, but how would you go about any-true??. Thinking about it mathematically, all-true? is basically, (not (any-false? ...)).

Lennart Buit19:11:24

(some ...) doesn’t completely cut it because it returns the first logical true item or nil

Lennart Buit19:11:13

maybe its me really wanting true/false instead of truthy/falsy

Alex Miller (Clojure team)19:11:35

you can use boolean instead of identity

Ben Grabow19:11:59

(some true? coll) sounds like what you're looking for, if you want to exclude truthy values that aren't true.

Alex Miller (Clojure team)19:11:07

(boolean (some boolean [false false]))

Alex Miller (Clojure team)19:11:27

if you are literally using booleans, then yes true? might be useful

Lennart Buit19:11:10

(every? identity [1 "abc" true]) is true, so then (boolean (some identity ...))

Lennart Buit19:11:56

would be the most faithful counterpart

Lennart Buit19:11:52

(Going from the domain of a collection of truthy/falsy values to a true or false)

didibus04:11:28

I prefer (every? true? [true false true]) since it's more explicit. Also, it depends if OP wanted truthy or true.

Lennart Buit07:11:20

My solution accepts truthy/falsy values and will only output true/false values 🙂. Kinda like: “Be liberal in what you receive and strict in what you send”.

Ben Grabow16:11:15

Is there a term for the idiom of returning a truthy value instead of returning true? I found "nil punning" for the opposite idiom, but I can't find "true punning" or "value punning". I'm wondering when it's best to return the truthy value vs returning true, and I want to read more about this practice. @U064X3EF3

Alex Miller (Clojure team)16:11:48

in the core api, that’s usually referred to as a “logically true” value

Alex Miller (Clojure team)16:11:05

but I can’t say I have anything for you to read about it

Ben Grabow16:11:21

Tough to google for "logically true" haha

Ben Grabow16:11:36

I've come across the idea that fns ending in ? imply a boolean return. Is that the common practice in core code?

Ben Grabow16:11:09

And I've seen reference to this idea in "The Joy of Clojure" but I don't have the book so I can't check. If the book talks more about this kind of style decision maybe I'll pick it up.

Alex Miller (Clojure team)16:11:28

these are often called “predicates” too

Ben Grabow16:11:33

*reference to the fact that the idea is discussed in "TJoC"

Alex Miller (Clojure team)16:11:00

you should pick up Joy of Clojure anyways :)

Ben Grabow16:11:06

A couple good reads I found: https://lispcast.com/nil-punning/ https://danielcompton.net/2017/03/31/clojure-nil-predicates The issue in the second link was indeed fixed to restore consistency across predicates returning true/false, so it sounds like about as consistent a rule as it can be.

Lennart Buit20:11:33

Hmm my biggest objection to nil is not that it doesn’t behave, but that it is a silent side effect of anything I do

Lennart Buit20:11:05

when, in haskell, a function returns Maybe a, I know damn well that it can fail

Lennart Buit20:11:19

Very much a fan of Rust consistenly marking functions that can fail with a Result type

didibus00:11:49

So, imagine you didn't have static types. Which is the case in Clojure. Now all functions can fail abruptly, those would still throw failure exceptions like say OutOfMemory. But now what about errors that are expected. Like illogical input, or things like can not compute. What would you want to happen?

didibus00:11:46

Let's go back to types. Why is there even errors? Because the type system isn't powerful enough. So a function returns a Maybe because you actually can't guarantee that it will work for all input.

didibus00:11:01

Now in Clojure, you have no types. Thus no functions are guaranteed to ever work for all input.

didibus00:11:22

Thus all functions always would have the type Maybe

didibus00:11:36

Okay, so what do we do? Well, we make all output from functions be a union of wtv the function can logically return and nil.

didibus00:11:16

Now that we recognise that all functions can return either Nil or wtv they logically return, you also need to recognise thus that every function composition will pipe a result of Either Nil or the logical result to the input of another function.

didibus00:11:07

Thus, you design all functions to handle Nil input. And BOOM, you now have Nil Punning

didibus00:11:16

Given functions which handle Nil input and can return the logical result or Nil, you can now compose freely and everything will just work!

didibus00:11:04

So what limitations does this have? Well there are two limitations to this in Clojure. First one is Java interop. Java doesn't do Nil punning, it throws exceptions instead for most things. So now you need to deal with both Nils and Exceptions for most contexts, which is more tedious. Second is that Nil doesn't tell you much of the root cause. So if you wanted to handle different type of errors in different ways, Nil isn't powerful enough. It would be pretty awesome if you could attach error types to Nil. In which case, it would be a lot more like things that return Error. But again, Java also return null sometimes. And so all this interupness means we're stuck acknowledging both Nil and Exceptions. So to me, it goes like this: Is there only one way to handle non logical result? Then return nil. If there are more ways, throw exceptions. Always design functions that handle Nil input, and always make sure you try/catch calls to functions that can throw.

Lennart Buit06:11:14

well yeah clojure doesn’t have a formal type system but you can still design by contract. If you promise that a function accepts x, y and z, and the pledge that it returns some maybe record. That would work. Not that the contract is checked ever, but it would make me as a consumer of that function aware of possibility of failure.

Lennart Buit06:11:09

Also, in haskell, Maybe a is Functor/Applicative/Alternative/Monad so you get fail-fast for free

Lennart Buit06:11:41

Java interop is indeed still tricky, because well, you have no control over whether a function returns nil or throws an exception

Lennart Buit06:11:19

In my experience, when a nil travels through my code, it ends up crashing somewhere :’)

Lennart Buit06:11:08

Much rather have to acknowledge that some code can fail, than have a nil that goes into another function that does something with that nil and returns nil and …

didibus06:11:09

I don't think you understood me. My point is that every function in Clojure returns Maybe a

didibus06:11:51

Where you either get "a", where a is not explicitly defined because we are in a dynamic context, but it can be thought as the implicit logical type of values you expect the function to return.

didibus06:11:11

Or you get Nil, which you can think of similarly to Nothing

Lennart Buit06:11:40

yes, but having all functions make no guarantees about whether it will return something defeats the point IMO. I personally, but maybe thats me prefering static typing, like to seperate ‘functions that can fail’ from ‘functions that cannot’.

Lennart Buit06:11:03

Meh, maybe I have to embrace it a bit more

didibus06:11:21

I mean, prefering sure, but what I'm trying to say is that you just can't have a similar thing in a dynamically typed context, because there's nothing to make such guarantee. So there's no difference between the doc-string saying Returns a or nil, versus Returns Just a or Nothing.

didibus06:11:15

Every function you see that mention they return nil in the doc-string is effectively a function that is declaring to you it returns a Maybe of a

didibus06:11:03

myDiv2 :: Float -> Float -> Maybe Float
myDiv2 x 0 = Nothing
myDiv2 x y = Just (x / y)
(defn my-div-2 [x y]
  (if (zero? y)
    nil 
    (/ x y)))
Those are the same

Lennart Buit07:11:29

Yeah sure. Cats has an either-type that you could return and make it a convesion to forbid methods returning nil

Lennart Buit07:11:39

but there is certainly no type checker to save you

Lennart Buit07:11:46

And then values are returned in Left or Right, so client code needs to treat it as such before it can do anything.

Lennart Buit07:11:52

You are probably right tho, its just a different way of thinking that I am not used to

didibus07:11:28

Well, that's the thing, sure cats has a Maybe type, but it really just feels useless, like extra ceremonious. And cats acknowledges that also, by having nil be equivalent to Nothing.

didibus07:11:01

So in cats, nil is Nothing. And you can neither enforce people check for nil or Nothing, neither can you specify your function returns a Maybe since there's no return type signature.

Lennart Buit07:11:48

Yeah, but with Haskell being my previous functional programming language, I have grown used to that ceremony. I really just have to embrace the dynamic nature of Clojure more

didibus07:11:51

Ya, it takes a bit of adjustment for sure. And then there's personal preference, how your brain thinks, etc.

didibus07:11:54

I think it could help if you start thinking about implicit return types. Such that say first returns nil if you give it an empty collection. In haskell, you'd say first returns a Maybe a. In Clojure, first returns a Maybe a also, where its Just the first element or Nothing (aka nil).

didibus07:11:37

The implicit nature though means it's up to you to realize that first can return Nothing (aka nil), and make sure you account for it and check that what you got isn't nil

didibus07:11:48

Now I think the difference is that in Clojure it's not Monadic, though libs like cats can make nil Monadic. What Clojure does is simpler, it allows nil to be composed normally, but most functions just pass it through, thus short circuiting on a nil. Where in haskell or with cats, you'd use the Monadic bind to compose the functions instead. Meaning other functions don't need to handle nil input, the Monad will deal with that and short circuit the chain for you.

didibus07:11:04

In the end you get the same behavior though

krchia19:11:46

is there a more idiomatic way to do (str (apply str (drop-last 4 thread-url)) ….)?

krchia19:11:08

how do you use subs to remove the last 4 chars?

hiredman19:11:14

user=> (def x "12345")
#'user/x
user=> (subs x 0 (- (count x) 4))
"1"
user=> 

krchia19:11:36

thats pretty nested too

krchia19:11:43

i’ll try that tho thanks!

hiredman19:11:27

it doesn't unnecessarily allocate a seq and build a string

mfikes22:11:51

It’s my hope that a future version of ClojureScript will be able to compile

(subs x 0 (- (count x) 4))
to a JavaScript expression that looks like
x.substring(0, x.length - 4)
In other words, extremely efficient, possible via type analysis.

noisesmith22:11:16

accepting -4 which is unambiguous, would also be nice (but less likely to be approved)

noisesmith22:11:56

well - it's not a breaking change, since until that is implemented any negative number would just be an error

schmee22:11:35

what does (subs x 0 (- (count x) 4)) compile to now in cljs?

mfikes22:11:27

count, in particular is a function call that involves a cond in its implementation

jaawerth23:11:17

@mfikes I know this was more in reference to using .length for calling count on strings but in that particular example you could also re-implement subs to use String.prototype.slice

jaawerth23:11:32

unless there's a perf cost there in JS engines I'm unaware of

noisesmith23:11:53

does slice share the data of the original mutable string ?

jaawerth23:11:03

strings aren't mutable in JS (IIRC)

jaawerth23:11:24

yep, strings themselves are immutable so no worries there

jaawerth23:11:09

I do remember one of the string methods being significantly faster than another method that can do the equiavlent, though. I just can't remember whether that was substring vs slice or something else.. just that it surprised me to learn of it

mfikes23:11:06

@jesse.wertheim a simple patch to subs could be considered—I suspect it would pan out if benchmarking works out and slice isn’t some newfangled thing only supported on recent VMs

jaawerth23:11:37

hrm, MDN says initial definition dates back to ES3 spec so you should be good there, but yeah I'm curious about the benchmarking

mfikes23:11:21

It’s relatively easy to benchmark a proposed change across VMs—the capability is built into the compiler tree

jaawerth23:11:56

incidentally I've been meaning to get the build set up on my machine so I can start dipping my toe into contributing

jaawerth23:11:57

main downside is that it's technically a breaking change, but I hope nobody is doing (subs x init some-negative-number) just to let it coerce to 0, lol

mfikes23:11:40

Ahh, I’ve always thought the arguments satisfy nat-int?. Hyrum…

hiredman23:11:49

subs accepting a negative number was rejected by rich for clojure many years ago, so if it was added to clojurescript that would be yet another difference between clojure and clojurescript

mfikes23:11:18

Ahh, well ClojureScript follows Clojure

jaawerth23:11:25

yeah that pretty much seals it

hiredman23:11:31

for some value of "follows"

jaawerth23:11:10

oh well, easy enough to make your own

jaawerth23:11:14

lol, I was curious enough about why Rich rejected it to look it up and found a HN thread full of names I recognize

jaawerth23:11:18

HN thread is rather punchy (shocker) so for the curious I'll link https://dev.clojure.org/jira/browse/CLJ-688 instead

andy.fingerhut23:11:08

People can get unpleasant when they disagree on technical topics (shocker)

andy.fingerhut23:11:50

Not that it is a good idea to get unpleasant about such things, of course. It does happen fairly often, though.

jaawerth23:11:21

Especially on HN ;-)

jaawerth23:11:12

Well, any forum that big

andy.fingerhut23:11:15

Evan Czaplicki in his talk "The Hard Parts of Open Source" proposes that perhaps having a discussion group that includes people who use a wide variety of programming languages exacerbates the issue: https://www.youtube.com/watch?v=o_4EX4dPppA

❤️ 4
idiomancy23:11:39

so, I'm timidly trying to move from lein to the new clj cli tools, and I'm going through this page... there are some things I dont understand: https://clojurescript.org/guides/quick-start > Now that we have a simple program, let’s build and run some ClojureScript: >`clj --main cljs.main --compile hello-world.core --repl` where is cljs.main coming from? should that be on my classpath? when I run that command, I get a java.io.FileNotFoundException: Could not locate cljs/main__init.class or cljs/main.clj on classpath. the clj command works fine and loads a repl

noisesmith23:11:47

cljs comes from the clojurescript library

mfikes23:11:00

It should be in specified in your deps.edn file

andy.fingerhut23:11:07

Did you download the standalone ClojurScript JAR that was linked near the top of that page?

andy.fingerhut23:11:03

oh, sorry, perhaps that was Windows-specific, according to those instructions. Ignore me

idiomancy23:11:53

got it. im a dummy. I needed to cd into the top level directory 😅

idiomancy23:11:03

deps.edn correctly doing its job

idiomancy23:11:22

lol, when everything is new and scary again, you panic quickly

idiomancy23:11:34

im so scared of approaching new tooling

andy.fingerhut23:11:13

Unexpected errors from new tools is confusing. No worries.

🙏 4