Fork me on GitHub
#beginners
<
2019-08-24
>
Danny01:08:23

I'm not sure if this is the appropriate place for this discussion, but I've been meaning to field test my understanding of some of Rich's justifications of clojure/spec. I'm rolling around the idea in my head that types and specifically the way we use them can essentially be broken down into far more basic ideas. We use type systems in languages such as Scala and so on to represent both the domain of our world with a common language and the data that exists in conjunction with that domain. Following this, we attach these representations of what are essentially just data shapes to functions all across an application attempting to document and describe what's going where, what's needed where, and how we use and transform from one shape to the next. That's a lot of fluff to say that we are simply using types as convenience in either A. holding the "grand-shape" of data in our heads and B. making assertions on the code that prevent simple type mistakes such as passing an int instead of a list of ints or failing to return the appropriate data shape from a particular function. That sounds awful like "documentation" and "tests". So, why not just write the tests or, rather, specifications about things flowing through functions. We can express even more this way. And, while we lose out on the benefit of being required to provide types (if they're inferred it's still good to place them), we gain the benefit of no longer needing to represent overly complex types that start to miss the point of typing in the first place (see Rich's Transducers talk). Of course, this assumes that while we work, we studiously are weaving this into our natural flow of development and have the tooling to support a sufficiently fast feedback loop to approach the benefits of compile-time "assertions". But, once we do, we can really get at the problems that types have been trying to solve while getting rid of the natural bulk and overhead of them. So, tldr (and suuuuper simplified); types are just DSL's for writing tests that run at compile-time, what's the harm in moving them provided modern tooling and development/build flows? What's missing in this argument.

4
jumar04:08:50

@dfehrenbach04 This might be better suited for Clojureverse or AskClojure

andy.fingerhut06:08:42

Yeah, one of those two venues would provide a longer history to others of viewing the answers. The short answer I keep from some of his talks is that every technique we use has benefits/advantages and costs/disadvantages (or if they have no benefits/advantages, we should not be considering them). Type systems have disadvantages that Rich finds to be a source of accidental complexity in his work, that he would prefer to avoid. spec plus generative/property-based testing are a way at getting at more of the properties of your code than today's type systems can specify (not guaranteed, but with high enough probability).

andy.fingerhut06:08:44

Or another variation on that, type errors in Clojure code tend to be fairly "shallow bugs", versus the deeper functional bugs that today's type systems often cannot help prevent.

๐Ÿ‘๏ธ 4
andy.fingerhut06:08:18

Note that these are my takes on his arguments -- not his arguments verbatim, so please do not represent what I say as his views.

drewverlee17:09:32

@dfehrenbach04 IMO, the whole topic is largely a matter of composition, abstraction and communication. At the highest level of abstraction we can consider some things true by definition (Category Theory, math). As we move towards solving a specific problem we quickly have to pick more and more detail (physics, computer hardware, etc..). As we focus in on our problem domain we can choose to pick up "domain" ideas (students, teachers, classrooms etc...). Type's should be a way of communicating relationships between idea's at these various levels. That communication is often useful at development time (where the compiler functions and where static types operate). But it's also useful for creating the application itself (e.g a spec error can easily become a user error, api return message, etc...). That is, that information is often useful at runtime. Spec lets choose when compile vs runtime and it also lets you choose how much you want to use it. I predict in 30 years (assuming we all make it that long) The norm will be languages that let you operate with more flexibility (gradual typing, compile vs run time, relaxed constraints at certain points, etc...)

4
unbalanced02:08:37

what do you mean by "moving them"? @dfehrenbach04

unbalanced02:08:14

Where I run into issues with types is higher order functions. I'm sure the folks who are super into Haskell have a good answer for this, but I have no idea how my favorite functions, map, reduce, and filter work in typed languages, especially without being extensible. The same goes for functional polymorphism. It also seems to defeat the whole point of "coding to an abstraction".

unbalanced02:08:06

At the very least I think it works kind of like... "upside down abstractions"

Danny02:08:10

"Moving them" as in moving when you see the error message from compile time to run time (when your tests "go")

unbalanced02:08:18

well I get the need for it, as in... there is a certain class of errors that can be prevented by using types. i.e. I've worked in large Python code bases where making changes is very scary because you have no idea what's going to break

unbalanced02:08:38

So you make a change, test as much as you can, and push to production ... only to find out two weeks later some page is down

unbalanced02:08:01

And sure, you can argue that "you should have tested better"

unbalanced02:08:27

So, yeah, if you're just running a script then compile-time or runtime-errors don't really make a difference, but if you're talking about systems with multiple entry points like a web application, the story is a little different, especially if you're iterating rapidly

unbalanced02:08:23

Where I really get hung up is reduce.

unbalanced02:08:47

how on earth are you supposed to say what the input types and output types of reduce are?

unbalanced02:08:47

same goes with transducers, it seems effectively impossible to type those, or at least it requires some level of string theory and tesseract geometry which is beyond me ๐Ÿ˜ฌ

unbalanced02:08:16

But I see the arguments for types. Rust makes a super convincing argument for types. Otherwise it's just too much a mental impedance for me ๐Ÿ˜ž

dpsutton03:08:25

Isnโ€™t reduce โ€œ(b -> a -> b) -> b -> [a] -> bโ€?

4
unbalanced09:08:58

You might be right. I've been thinking about it in terms of Rust which (I think?) has a more concrete type system.

Yosevu Kilonzo04:08:25

Hello #beginners! I'm new to Clojure and trying to learn by following this post to create a blog: https://cjohansen.no/building-static-sites-in-clojure-with-stasis/. I'm currently running into this error when I start the server: Caused by: java.lang.RuntimeException: No such namespace: str. Any idea how do I use the str namespace?

dpsutton04:08:39

Are you familiar with how to require namespaces from other namespaces?

dpsutton04:08:27

If so, most likely you can put [clojure.string :as str] in your ns form

๐Ÿ‘Œ 4
Yosevu Kilonzo12:08:31

Thanks @dpsutton ! I need to read about how namespaces work ๐Ÿ˜…

hamid tsh07:08:32

hi, how can i add autoreload by speficic time in figwheel project?

st3fan14:08:30

I created a little ring app! My first Clojure after playing with it a long time ago.

st3fan15:08:00

Right now I just edit files in Emacs (Prelude) and when I save the whole thing reloads. Good stuff, but I want to move to REPL based development, so that I can more easily change things.

st3fan15:08:26

Can anyone give me a hint how I move from lein ring server-headless to running my server in the REPL?

David Pham15:08:17

Usually, I run lein ring and lein repl and connect to lein repl

David Pham15:08:14

I then connect cider to lein repl (with cider-connect-clj)

David Pham15:08:43

You can then use you repl to develop your functionality and normally lein ring can auto update

st3fan15:08:08

oh that sounds simple

st3fan15:08:33

so you donโ€™t start your app inside the repl, you just run lein ring server in a separate window/session next to it

nmkip15:08:02

If someone knows about clojurescript and reagent, please check the #clojurescript channel because I'm having a #beginners problem and wasn't sure wether to ask there or here ๐Ÿ™‚

David Pham15:08:41

@st3fan yes that is about it.

David Pham15:08:13

Obviously, there are some drawbacks, but, it forces me to write functions I can test in the repl, and I test ring by sending request inside my repl xD

David Pham15:08:37

Not sure if it this is the best, but I have a good workflow with that.

st3fan15:08:49

yeah great - i will try the same

David Pham15:08:22

In ClojureScript, how do you use react component that rely heavily on mutability? I have a library (react-chart-editor) that I would like to use, but the components never rerenders whenever the state change. I use reagent. I tried to bypass CLJS completely as well, using react-dom, but I did not get a better solution.

st3fan16:08:53

Getting pretty excited about Clojure again but there are many things i donโ€™t really get yet

st3fan16:08:23

Is https://github.com/dakrone/clj-http a good HTTP client to chat with REST APIs?

David Pham16:08:11

@st3fan this is the one I use for testing my ring server

theSherwood16:08:35

This may be the wrong place to ask this question. I'm interested in giving clojureScript a try. I've liked a lot of what I've seen and read about it. Is there currently a way to get up and running with clojureScript without installing java? What is the current best option for this?

seancorfield16:08:32

@adam.the.sherwood ClojureScript's compiler requires the JVM (so, no, you need "Java" installed -- at least a JRE or JDK).

lispyclouds16:08:27

There is some experimental support on Lumo. I'm no expert on cljs, but it seems to work: https://github.com/anmonteiro/lumo#compile-clojurescript

seancorfield16:08:56

Yeah, I was about to add the caveats of lumo/plank/etc...

theSherwood16:08:02

Ahh. Okay. Thank you both. Is Lumo reliable as far as you are aware?

lispyclouds16:08:21

Its quite stable/mature as a Node.js platform as far as i know. has frameworks like https://macchiato-framework.github.io/

lispyclouds16:08:08

but the obvious issue one might have when using this as the sole cljs platform is the lack of tooling like leiningen/boot/tools.deps. specially when building frontend apps. they all need the JVM and pretty much make the cljs frontend dev experience what it is ๐Ÿ™‚

David Pham16:08:42

Why would you avoid Java?

David Pham16:08:06

I use shadow-cljs, and I donโ€™t see any difference.

theSherwood16:08:05

I'm just wanting to avoid complexity. I'm comfortable around javascript, but don't feel like grappling with java in addition to learning cljs

David Pham16:08:39

You donโ€™t need to learn any java for running shadow-cljs

4
David Pham16:08:10

I would say knowing npm and the JS Ecosystem would provide a much bigger advantage

theSherwood16:08:14

great! I'll have to do some research!

David Pham16:08:50

npm install -g shadow-cljs

David Pham16:08:51

Helps for starting with the config file

theSherwood16:08:47

That's great help. I'll have to give it a try!

David Pham16:08:23

Good luck and welcome in the community :)

theSherwood16:08:23

Thank you all very much! Very helpful

sova-soars-the-sora18:08:39

I'm learning how to parse XML. I want to emit a clojure datastructure

sova-soars-the-sora18:08:06

some of the XML properties are multiples, is it alright to just cram them all into a sequence [to ge ther] ?

andy.fingerhut18:08:57

as in, you are writing your own XML parser in Clojure?

sova-soars-the-sora18:08:23

I am. I want to read a big XML file and create a clojure datastructure from it.

andy.fingerhut18:08:29

No problem if that is what you want to do. There are multiple XML parsers written in Clojure and Java, if you want to use those instead.

sova-soars-the-sora18:08:37

oo that's even better

sova-soars-the-sora18:08:56

i like the way you think. find an open source bicycle instead of building my own.

andy.fingerhut18:08:02

They all produce their own data structures as a result, and not all of them will produce the same things.

andy.fingerhut18:08:39

As I said, it is certainly fine if your goal is to learn more about Clojure and/or XML by writing your own.

sova-soars-the-sora18:08:42

hmm well it's a huge file you see, it's 50 Megabytes, it's a Japanese Dictionary, and I have concluded that each entry can simply be a {:map "withKeyValPairs"}

sova-soars-the-sora18:08:43

thanks i'll take a look at some and see what makes sense

sova-soars-the-sora18:08:12

Any recommendations?

sova-soars-the-sora18:08:10

Zippers look tremendously useful

Ashley Smith19:08:07

I'm trying to create a li element using the javascript interop, been following this: https://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/ but it talks about JS objects and I'm not quite sure about the difference between those and DOM elements? I'm working with TaggleJS https://sean.is/poppin/tags and I'm trying to format the outputted tags.

:tagFormatter (fn [li]
                      (let [children (.-children li)]
                        (.log js/console children)))
I need to pass a function which takes the li, manipulates it and it's children, and returns a new li for taggle to use.
HTMLCollection { 0: span.taggle_text, 1: button.close, 2: input, length: 3, โ€ฆ }
forms.cljs:121:30
I've tried accessing these children with (:0 children) and (get children 0), but using .log js/console isn't giving me anything but null. Any ideas? My end goal is to simply manipulate the classes of the text and the close button, as well as adding another span in the middle. Sound doable?

Ashley Smith19:08:56

Update: Realised that I needed to use JS functions to work with JS stuff, so here's the current function. I'll neaten it up as I learn more.

:tagFormatter (fn [li]
                      (let [children (.-children li)]
                        (.add (.-classList (.item children 0)) "tag")
                        (.add (.-classList (.item children 0)) "is-dark")
                        (.add (.-classList (.item children 1)) "tag")
                        (.add (.-classList (.item children 1)) "is-delete")
                        
                        ))

Ashley Smith19:08:25

Looking good now! Just got to use CSS to style the rest. I got it ๐Ÿ™‚

Vesa Norilo19:08:57

when you find yourself doing a lot of java/js interop, .. and doto are some handy macros

Ashley Smith19:08:13

I'll look them up, thank you!!

Ashley Smith19:08:25

I apologise for the horrible formatting of my paste ๐Ÿ˜ž

Vesa Norilo20:08:50

warning: didn't check or run this so could contain errors but those two applied to your case:

(doto (.. li -children (item 0) -classList) 
  (.add "tag")
  (.add "is-dark"))
(doto (.. li -children (item 1) -classList)
  (.add "tag")
  (.add "is-delete"))

Ashley Smith20:08:42

I'm actually just going to make it myself in clojure and maybe even release it publicly if its clean

4
๐Ÿ˜Ž 4
Ashley Smith20:08:48

as TaggleJS is not very nice

Ashley Smith20:08:02

too much has been done for you, not very customiseable

sova-soars-the-sora16:08:46

Probably just a side-effect of not being made in/of LISP. Although Javascript gets pretty damn close with callbacks ... at some point the rigidity of expression causes kinks in the chain

Ashley Smith01:08:12

I really hated TaggleJS so I rolled my own thing. Here's what I came up with! There's no autocomplete yet, but there's some easy room for expansion now that I did it my own way ๐Ÿ™‚

Andrea Imparato21:08:49

hello, could anybody give me some hints on how to write tests loop/recur functions?

dpsutton21:08:32

they can be tested the same as any other function

Andrea Imparato21:08:18

yeah could be because I'm trying to test a function that I wrote for a tic tac toe game

Andrea Imparato21:08:25

let me paste the function

Andrea Imparato21:08:37

(defn -main [& args]
  {:pre [(or (spec/valid? ::args args)
             (spec/explain ::args args))]}
  (loop [game-state (-> (config/read-config-file (first args))
                        (game/initialize))]
    (grid/print-grid game-state)
    (when (nil? (:metro.game/winner game-state))
      (recur (game/play game-state)))))

Andrea Imparato21:08:13

how would you test this function?

dpsutton21:08:06

i would pull the initialization and config reading out

dpsutton21:08:54

and there's really not much to test here. the logic allows for no hidden things. what you really want to test is (game/play game-state)

dpsutton21:08:32

but really there's some function (move game-state player-input) somewhere in your code that you want to test. the rest is printing and just a loop

Andrea Imparato21:08:05

so you think testing the loop itself doesn't make really sense?

Andrea Imparato21:08:29

since there's really not much logic there

dpsutton21:08:46

its print, if winner stop, else play. there's simplicity in it and no edge cases. not sure what a test would give you

dpsutton21:08:38

and all it would test would really be that (game/play game-state) correctly sets a winner if indeed there is a winner

Andrea Imparato21:08:18

yeah I actually think the same, I just wanted to find some confirmation ๐Ÿ™‚

Andrea Imparato21:08:25

thx for the help @dpsutton ๐Ÿ™‚

nmkip22:08:25

Is this an ok guide to enter the REST world in clojure? https://medium.com/swlh/building-a-rest-api-in-clojure-3a1e1ae096e

Nicholas Griffen23:08:03

let us both follow it and let the world know

Nicholas Griffen23:08:14

I think it is not quite right to call this a REST tutorial, because all request are mapped to a single HTTP verb ( GET )

nmkip00:08:37

yes, I noticed that too. I was wondering if the libraries they use there are the 'mainstream libraries' for this kind of project

4