Fork me on GitHub
#announcements
<
2022-01-11
>
mpenet13:01:34

Initial (early) release of https://github.com/exoscale/lingo - a lib that extends spec explain-data and helps with formatting error messages from these extensions

💪 5
🎉 13
😍 3
😎 3
👏 3
borkdude14:01:34

Perhaps a section on how it differs from expound could be useful?

1
borkdude14:01:36

When I try to load it with bb I get:

$ bb -cp $(clojure -Spath -Sdeps '{:deps {org.babashka/spec.alpha {:git/url "" :git/sha "1a841c4cc1d4f6dab7505a98ed2d532dd9d56b78"}}}') -e "(require '[exoscale.lin
go])"
----- Error --------------------------------------------------------------------
Type:     clojure.lang.ExceptionInfo
Message:  Cyclic load dependency: [ exoscale.lingo ]->[ exoscale.lingo ]
Why does the namespace load itself?

borkdude14:01:55

if I remove that it works:

$ bb -cp ... -e "(require '[exoscale.lingo :as l] '[clojure.spec.alpha :as s]) (s/def ::int int?) (l/explain ::int :foo)"
1 error found

:foo is an invalid :user/int - should be an Integer

mpenet14:01:58

clj-kondo didn't warn me about this one, your fault 🙂

😆 4
borkdude14:01:05

touché :)

2
mpenet14:01:13

I ll release a new version now

mpenet14:01:36

yeah, it's intentionally no-dependency, runs in bb and I guess in cljs without too much trouble

mpenet14:01:32

I didn't mention expound directly but I do mention that lingo is more data centric, expound works on explain and explain-str

mpenet14:01:52

lingo focuses on explain-data and makes this the base for everything else

mpenet14:01:11

expound has no (exposed) intermediary data representation in short (afaik)

mpenet15:01:43

another thing, expound does not try to give a way to interpret predicates from the user side. Every custom message has to be applied on a registered spec and if/when that spec rejects a value it will just return that message, there's no predicate context

mpenet15:01:02

no "parameterized" error if you want

👍 1
mpenet15:01:41

I wanted to have this added to expound but the author didn't like the idea. hence lingo

borkdude15:01:56

all makes sense

ikitommi15:01:35

looks great! have you checked how this effects the js-bundle size?

mpenet15:01:27

no clue, I am not even sure the current code runs in cljs (it should but I didn't test it). It's a very early release, in many ways, there are a lot of perf improvements that are possible left & right as well, the current impl. for pred parsing is quite naive for instance.

mpenet15:01:07

but all that will come in time

mpenet15:01:50

please don't benchmark it against malli now 🙂 that would hurt I am sure

ikitommi05:01:20

keeping js-bundle size small is not easy, defs & multimethods can’t be DCEd and anything they use will be dragged in too, e.g. regex-schemas etc. Not a concern in most apps, but still a concern.

ikitommi05:01:54

at some point, want an expound-grade prettier printer for malli too, current just Fipps out with colors, not marking errors / hiding details on large errors. Good to see more development in this space.

borkdude07:01:50

If you want a small Js bundle then don't load spec(s) and move them to the side

mpenet08:01:24

yeah, arguably that should be mostly for dev time usage in cljs, or cases where you just don't care.

mpenet08:01:07

I thought about using fipp, but for now I like being dependency free. But I also know pprint is crazy slow, so we'll see

mpenet08:01:19

it would be nice to have spec/select spec/schema as a library until spec2 settles (if ever), I suppose that could mitigate some of that

borkdude08:01:43

there are some tricks to remain dependency free + optional namespaces which, when loaded, register some "plugin" which, if available, can be used for certain features.

mpenet08:01:05

yes I did use that in other projects, but not with cljs

mpenet08:01:19

it's a bit hairy tho

mpenet08:01:51

depending on what the lib does and its size, just copying the code is often better imho

mpenet08:01:16

(with attributions of course)

borkdude08:01:26

another option is to provide the dependency function just an as argument option and you can program against some protocol

borkdude08:01:20

I do a similar plugin thing in #nbb where cljs.pprint is compiled into a different module than the rest of the stuff. So when you don't use cljs.pprint you don't load that 200kb of JS all the time. Everything is advanced compiled.

mpenet09:01:48

I think I might just throw in a try requiring-resolve .... around fipp fn and be done with it

mpenet09:01:05

well, I need to check if requiring-resolve actually works with cljs

borkdude09:01:52

and it's not so good with graalvm native image either. This is a better approach: https://github.com/seancorfield/honeysql/pull/378/commits/e331ba0a0e44bec5da7e094f0c699181dd8d6cde

mpenet09:01:44

thx for the pointer

borkdude09:01:21

I.e. do the resolving on the top level. For CLJS you will have to require people to load a namespace and in that namespace you can register whatever function you need. If that functions isn't available (due to the namespace not having loaded) then you assume the user doesn't want to use that dep.

borkdude09:01:49

CLJS namespaces are static.

borkdude09:01:11

And only top level ns forms (or require, but to simplify ignore this) are allowed

borkdude09:01:46

Here is another solution that works in both CLJ and CLJS: https://github.com/borkdude/dynaload

borkdude09:01:13

but I prefer the namespace solution, that simplifies things drastically and also works in babashka even.

borkdude09:01:31

(ns core-ns)
(def optional-function (volatile! nil))
(defn api-fn []
  (if-let [f @optional-function ...] ...))

(ns optional-ns (:require [core-ns] [pprint]))
(vreset! core-ns/optional-function pprint/pprint)

borkdude09:01:32

README: to use pprint, require optional-ns in addition to core-ns.

mpenet09:01:31

I ll have a look tonight if I can do something like that

ikitommi09:01:15

just getting the humanized error messages from specs is actually useful in normal CLJS apps, e.g. with form validation. Seen libs like https://github.com/alexanderkiel/phrase used there.

mpenet09:01:45

yeah, I have seen that one as well.

mpenet09:01:20

I personally prefer to stay close/extend explain-data output, it's easier to port to it and also staying at the explain-data.problems[] level makes it easier to use in various contexts (web-form rows, cli, backend etc). But the more the merrier. All these libs are not a lot of code.

👍 1
mpenet09:01:37

That said it would be nice if the focus/highlighting part could be shared

mpenet09:01:15

the ~^ stuff

mpenet09:01:41

and possibly other bits

ikitommi10:01:39

yes, the ^ stuff. did a placeholder repo 3y ago for just pretty error printing lib, but no time, have copy-pasted the same fipp-stuff between libs (reitit, malli). Modular, but incomplete (and works with expound) https://github.com/metosin/virhe

mpenet10:01:25

yeah fipp api is quite nice for this

emilaasa12:01:13

Looks real neat! Will try it out