This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # beginners (12)
- # calva (18)
- # cider (1)
- # cljs-dev (29)
- # clojure (97)
- # clojure-uk (18)
- # clojurescript (10)
- # clojureverse-ops (2)
- # cursive (7)
- # emacs (10)
- # fulcro (42)
- # graphql (36)
- # joker (1)
- # juxt (28)
- # mount (2)
- # other-languages (2)
- # pathom (1)
- # portkey (3)
- # re-frame (50)
- # shadow-cljs (42)
- # spacemacs (4)
- # sql (6)
- # yada (6)
Type systems are becoming more expressive:
Dependent types allow for support of contract-like constraints.
The use case of sub-selection for
spec schemas can already be handled by structural types and row polymorphism, as in Typescript and Elm.
If you’re comfortable with using
spec for that particular use case, then you’d be comfortable using static types in Clojure.
Can you talk about this more? What you mean by sub-selection? What is the use case for it, can you give an example?
check out Rich’s Maybe Not talk. he discusses having a spec schema from which you can select a subset of fields and provides motivation
Okay, maybe I thought you were responding to me. That's why I got confused. But now I think you were talking to todo.
anyway, happy to provide pointers to any type-related info. I’ve been a long-time Clojure user and espouser of dynamic types. over past few years my opinion on types has changed based on experiences working on large Clojure and Python projects
I still use Clojure daily for work and hobbies, but starting to switch over to Haskell for some things
If you were talking to me then, I was talking about full strictly static type systems. Not optional. Where every single construct needs to be typed. I still feel that doesn't work well with Lisps. Dependent types add a huge annotation burden which would defeat the Lisp dynamic interactive live programming flow, and make the sexpr syntax even more of an eye sore I feel. Hindley Milner typed can't type all of Lisps constructs, and if you changed Lisps so it could, it's no longer Lisp, but a weird sexpr like syntax for Haskell. The only types I think could work are simple systems like Java's type system. Common Lisp has support for those optionally for example and that works fine.
yeah, I agree with most of that and especially the optional portion. That’s the direction I wish Clojure was headed, optional/gradual typing
I wasn't so much talking about being pro static or dynamic, but more that this holy grail everyone wants, which would be the dynamism of Lisp and the compiler checks of Haskell I think is a pipe dream, at least for now, because they literally oppose each other. What is great of Lisp is dynamism, which invariably means you can't have strong static guarantees. So you need to choose which one you prefer.
it’s kind of frustrating to see
spec continue as a runtime-only checker when other languages can statically check the structural types that seem to be the main motivation behind
mm, so you can have specs that act as contracts. where one can use arbitrary predicates, and I agree those can’t be expressed in most type systems
but look at Rich’s talk, and the specs he uses as examples can be statically checked. proof being Typescript, Elm, and OCaml
Hum... I don't remember the details, but his whole spiel was that Haskell and other strongly typed system did it wrong, and so did he with spec 1
It was more of a criticism of being forced to define things where they aren't in the domain I felt.
Haskell does have the ability to express structural types, but it is a little bit hacky. The languages I already mentioned: Typescript, Elm, and OCaml; are statically typed and do have solid support for structural types and handle the issue Rich brought up
Problem is, different time or states have different requirements of what could be in it.
so the keywords to search for are “row polymorphism” and “structural types”, but as for how they work, it’s similar to what’s coming in spec2. You have some type schema/definition, and for a function that only requires a subset of the fields defined in the schema, the type/spec for the function parameter is defined as a subset of the schema
Ya, I guess that was his insight. And Haskell doesn't support them first class, which was his criticism
in Haskell, the closest reasonable solution is probably lenses. but that isn’t really row polymorphism. if you ask Haskell folks for three or so top issues to fix in the language, records/row types will be one of them
there’s some info on row polymorphism in OCaml. maybe that helps? as to what the type checker is doing, I don’t know
So, say you have a record of [:a :b :c]. And there's a function that needs [:a :c] and its given [:a :c :x :y] as input. And say it returns [:a :b :c], how does it know these are all compatible types?
They talk how each approach has trade offs, and neither can perfectly give us the behaviour we want, but that's the compromise of static type systems often time, you adapt to the way the type checker works in order to rake its benefits
"For example, lists of heterogeneous elements cannot be created using row polymorphism"
Like that's a pretty big blocker for Clojure and most Lisps. Since they're most primitive construct are heterogeneous lists
Hum... are you sure? They don't make it sound like it. It seems to be a limitation of row polymorphism. Subtyping can do it, but then things can't be type inferred and needs to be explicitly casted it seems.
I honestly don’t know enough about OCaml to discuss it. =/ Let me do some reading and see if there’s an example of a type system without that limitation
thanks for discussing it, I wish that happened more here! it’s good to run into the limitations
Ya, thanks to you too. I like both static and dynamic languages. For different reasons. I prefer using certain dynamic langs, but not all. Only Lisps really, otherwise I prefer statically typed languages. I'm more experienced with C++, Java, C#. But have learned Haskell amateurishly. In general though, I find every static system is restrictive in one or another way. That's inherent from the problem. How do you teach a computer to understand and reason about code in a way that it can catch the bugs in it, when you couldn't. Nobody would say no to that, if it didn't come with trade offs. And even between strategies for achieving this there are many trade offs. So I tend to be skeptic of the static type advocates that don't start with talking about limitations and sacrifices of their approach 😋
That doesn't mean sometimes the trade isn't worth it. But you need to accept that you will model things, not necessarily the way that you want to model it, not necessarily the way that is most intuitive, and not necessarily the way that the domain has it, but the way the type checker needs it.
At the end of the day, for me, it comes down to what I'd rather do and who I trust more. Could I personally catch more bugs if I was modeling it my way, and having fun doing it? Or would the type checker catch more bugs if I modeled it its way, and felt frustrated throughout the process 😋
So in practice, I found I do better with Clojure over Java,C#,C++, but I do worse with Python, JS and Ruby.
I guess though, I need to give Haskell, OCaml and PureScript a real good try to see if I end up doing better with them
The biggest hurdle for me with Haskell was getting comfortable with the handling of Effects through types. OCaml or others that aren’t pure (i.e., monads or some other effects system isn’t required) are maybe a good place to start exploring
When doing a defrecord or type while implemening a protocol, does it matter if your implemented function use the "this" binding or the bound variables in the protocol definition?
Cross-posting the underlying question from #cursive.
I have a code that calls function with some not null arguments, but one of the arguments becomes null in the function's body.
Here's a piece of code of interest, it's from
During debugging, when the current line is the very first one,
(defn write "Writes a value to a transit writer." [^Writer writer o] (.write ^com.cognitect.transit.Writer (.w writer) o))
writeris not null. But as soon as I step into , it becomes null for no apparent reason. How can such thing possibly happen?
Nevermind that - I got confused by the inconsistently highlighted stacktrace, and the reported null value is not null in reality.
Hi! Is there a readline lib that I can use to build completion and history function on terminal input? Something similar to https://github.com/chzyer/readline
@dominicm No Clj lib available ?
bhauman/rebel-readline, but couldn’t find a simple example of how to use it for my use case. It’s mostly documented for REPL use.
@markx You shouldn't hesitate to use Java libs from Clojure. It's part of the power that Clojure brings to make that easy and possible. Rebel-readline uses JLine itself.
When I'm debugging, I add
tap> calls (instead of
prn/`println`), and eval the top-level block (to redef the function I just changed), and then run whatever scenario I need and see what crops up in REBL.
I know you use rebl right. Wondering if other people do the same but just wire it to prn
Before using REBL, I used to connect an additional Socket REPL and telnet into it and add a tap watcher that
While it's safe to leave it in, I don't see much point in having
tap> calls in production code.
If you need tap-debugging in production, you can always connect to the process, eval in new function definitions with
tap>, do the debugging, and then eval in the original version again.
(mind you, if you're running with direct linking enabled, that workflow doesn't always work, unless you re-eval further up the call chain).
Is there a way to pretty-print the output of
macroexpand-1 to make it more readable than 1 long line? Thanks!