Fork me on GitHub
#clojure
<
2020-02-14
>
dvingo00:02:43

I'm struggling with a closed stream using clojure.data.csv/write-csv to write to stdout:

(defn -main [& args]
  (with-open [w (io/writer *out*)]
    (write-csv w [[1 2 3] ["a" "b" "c"]])))
invoked with: clojure -m 1,2,3 a,b,c Exception in thread "main" http://java.io.IOException: Stream closed any ideas?

noisesmith00:02:38

with-open closes its input on exit, usually you don't want to close *out*

noisesmith00:02:20

@danvingo I tried it in a repl, and replicated the error - if I make a writer out of *out*, then close it, then clojure crashes the next time it tries to use *out*

Clojure 1.10.1
(ins)user=> (def w ( *out*))
#'user/w
(ins)user=> (.close w)
Error printing return value (IOException) at sun.nio.cs.StreamEncoder/ensureOpen (StreamEncoder.java:45).
Stream closed
Execution error (IOException) at sun.nio.cs.StreamEncoder/ensureOpen (StreamEncoder.java:45).
Stream closed

noisesmith00:02:34

the solution is don't use with-open, because you don't want to close it

rodolfo00:02:46

☝️ Also, you can also use *out* directly, since it's already a .Writer :

(write-csv *out* [[1 2 3] ["a" "b" "c"]])
That way you don't need to .flush it, which you would need if you only made a writer from *out* (i.e. replaced your with-open with a let)

dvingo00:02:14

ah that makes sense about not closing stdout. Thanks rodolfo, using **out** direclty worked!

👍 4
pinkfrog06:02:29

I have a function named page that returns a vector containing two elements. For example, [[:head some-val] [:body some-val]]

pinkfrog06:02:43

I want to pass it to (hiccup.page/html (page))

pinkfrog06:02:00

however, hiccup.page/html5 accepts a variadic args. So I tried, (apply hiccup.page/html (page))

pinkfrog06:02:12

but since hiccup.page/html5 is a macro, so the above failed.

pinkfrog08:02:36

I come up with defining another macro to manipulate the form with ~@

orestis09:02:33

I’m looking for a better way to log what a function is doing in production. That is, I want a better time macro that can “snapshot” between various parts of a function and then I should be able to control the logging of the results. I don’t want a benchmarking tool, rather something that will collect fine-grained metrics.

dharrigan09:02:07

also they have a function-instrumentation example just below the code block instrumentation

orestis09:02:50

Hm, I was more interested in using something that I can weave through a let binding. Mostly for debugging to diagnose network/database bottlenecks.

orestis09:02:18

Here’s my crude approach:

(defn start-stopwatch []
  (atom (jt/instant)))

(defn capture-time [time-atom msg]
  (let [now (jt/instant)]
    (if-let [before @time-atom]
      (log/info :msg (str "Time! " msg) :time-ms (jt/time-between before now :millis)))
    (reset! time-atom now)))

orestis09:02:44

I should probably use a monotonic clock like (System/nanoTime)

kwladyka10:02:59

There are solutions like:

(metric-wrap
(do-something)
(do-something)
(do-something))
and metric-wrap will send data to metric system about time to send metrics. You have to only choose metric system and probably you will find library for this.

sonnyto10:02:35

can anyone answer this about where STDOUT of native C lib redirected to in the Clojure REPL? https://groups.google.com/forum/#!topic/clojure/FzM3tIphBUw%5B1-25%5D

noisesmith17:02:10

This depends on what repl you use. For generic clojure.main repls, it will go to the STDOUT and your repl also prints to STDOUT. For a client/server like NRepl (which eg. cider uses) it will go to stdout of the server process, which your editor might be hiding in an unexpected place.

😎 4
sonnyto19:02:04

yeah Im using cider

cddr10:02:24

I have a reflection warning I'm trying to resolve but the message includes a line number that is just the first line in the file which makes it difficult to track down the actual problem. Reflection warning, jackdaw/test/serde.clj:1:205 - call to java.lang.IllegalArgumentException ctor can't be resolved. Anything I can do to get a better idea of where the problem is?

noisesmith17:02:51

I vaguely recall that this can mean an error in the ns form of some other ns required from that file - could be wrong though.

penryu10:02:04

if you ignore newlines from serde.clj, does offset 205 point to any specific form?

cddr10:02:37

Ah I'll try that

penryu10:02:36

Additionally, look for any java iterop calls that might not map to valid java method overrides.

cddr10:02:40

Hm, I don't think so. M-x goto-char 205 leaves me in the namespace declaration.

penryu10:02:23

Is the (ns ...) form valid? 🙂

cddr10:02:43

I think so. Unless there's something weird related to Generics. The Serde below is actually a Serde<T>.

(ns jackdaw.test.serde
  (:require
   [clojure.tools.logging :as log]
   [jackdaw.serdes.edn :as edn-serde]
   [jackdaw.serdes.json :as json-serde])
  (:import
   (org.apache.kafka.clients.consumer ConsumerRecord)
   (org.apache.kafka.common.serialization Serde
                                          Deserializer Serdes Serializer
                                          ByteArraySerializer
                                          ByteArrayDeserializer)
   (org.apache.kafka.common.errors SerializationException)))

penryu11:02:05

Nope. Serde being generic shouldn't be related to importing the symbol. Doesn't look like there's enough information present to help debug.

cddr11:02:46

OK. Thanks for the suggestions. I guess I'll skip this one for now. There's 100s of others to fix 🙂

didibus01:02:18

Why you bothering type hinting everything? It generally barely adds any overhead, unless in a loop

cddr18:02:44

The warnings produced when loading the library are getting a bit overwhelming. I’d be interested in knowing how to selectively enable the warnings to cover only performance sensitive parts of the code base but didn’t think that was an option

didibus20:02:55

Hum, you can set warn-on-reflection on for only a subset of the code

didibus20:02:16

To reduce the number of warning

didibus20:02:26

I'd also recommend profiling first.

KJO12:02:48

I'm stumped on this. I have a function that returns a lazy sequence (let's call
it lzs - it's produced by another function, and for this exercise I def'd it
in the REPL from `*1'). It's a sequence of maps.

When I apply a merge-with operation to the lazy sequence. I get the following
error.

  class clojure.lang.PersistentHashSet cannot be cast to class 
  java.lang.Comparable (clojure.lang.PersistentHashSet is in unnamed 
  module of loader 'app'; java.lang.Comparable is in module java.base 
  of loader 'bootstrap'

If I apply the same operation on a `doall' of the sequence, I also get the error.

However, if I fully realize the sequence in the REPL and feed it into the
merge operation, I don't get the error. There's really no magic in the merge-with
operation, it just conj's some sets.

Any ideas?

The relevant code is below:
;; FAILS
(apply
  merge-with
  px-tran-netter-ex
  lzs)

;; FAILS
(apply
  merge-with
  px-tran-netter-ex
  (doall lzs))

;; NO PROBLEM HERE with lzs realized and fed back in
(apply
  merge-with
  px-tran-netter-ex
  '({:fund {:src-ax-id #{nil :ax-m01}}}
    {:fund {:src-ax-id #{:ax-m01}}}
    {:fund {:src-ax-id nil}}
    {:fund {:src-ax-id :ax-m01}}
    {:fund {:src-ax-id #{:ax-m01}}}
    {:fund {:src-ax-id #{nil :ax-m01}}}
    {:fund {:src-ax-id #{:ax-m01}}}
    {:fund {:src-ax-id nil}}
    {:fund {:src-ax-id :ax-m01}}
    {:fund {:src-ax-id #{:ax-m01}}}))

The px-tran-netter-ex is 

(defn px-tran-netter-ex
  [ov nv]
  (if (nil? ov)
    nv
    (update
      ov
      :src-ax-id
      (fn px-tran-set-merger
        [lv rv]
        (if (some? rv)
          (conj
            (if (set? lv)
              lv
              (if (some? lv)
                #{lv}
                #{}))
            rv)
          lv))
      (:src-ax-id nv)))) 

p-himik12:02:11

What is the stacktrace of that exception?

KJO12:02:35

If this helps, here it is. The original sequence is returned by a `for' 

#error {
 :cause "class clojure.lang.PersistentHashSet cannot be cast to class java.lang.Comparable (clojure.lang.PersistentHashSet is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')"
 :via
 [{:type clojure.lang.ExceptionInfo
   :message nil
   :data #:clojure.error{:phase :print-eval-result}
   :at [clojure.main$repl$read_eval_print__9086 invoke "main.clj" 442]}
  {:type java.lang.ClassCastException
   :message "class clojure.lang.PersistentHashSet cannot be cast to class java.lang.Comparable (clojure.lang.PersistentHashSet is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')"
   :at [clojure.lang.Util compare "Util.java" 153]}]
 :trace
 [[clojure.lang.Util compare "Util.java" 153]
  [clojure.lang.RT$DefaultComparator compare "RT.java" 283]
  [clojure.lang.PersistentTreeMap doCompare "PersistentTreeMap.java" 330]
  [clojure.lang.PersistentTreeMap entryAt "PersistentTreeMap.java" 317]
  [clojure.lang.PersistentTreeMap containsKey "PersistentTreeMap.java" 97]
  [clojure.lang.APersistentSet contains "APersistentSet.java" 34]
  [clojure.lang.PersistentTreeSet cons "PersistentTreeSet.java" 68]
  [clojure.lang.PersistentTreeSet cons "PersistentTreeSet.java" 17]
  [clojure.lang.RT conj "RT.java" 677]
  [clojure.core$conj__5390 invokeStatic "core.clj" 85]
  [clojure.core$conj__5390 invoke "core.clj" 82]
  [user$px_tran_set_merger invokeStatic "scratch.clj" 202]
  [user$px_tran_set_merger invoke "scratch.clj" 197]
  [clojure.core$update invokeStatic "core.clj" 6198]
  [clojure.core$update invoke "core.clj" 6188]
  [user$px_tran_netter invokeStatic "scratch.clj" 219]
  [user$px_tran_netter invoke "scratch.clj" 215]
  [clojure.core$merge_with$merge_entry__5962 invoke "core.clj" 3063]
  [clojure.core$reduce1 invokeStatic "core.clj" 944]
  [clojure.core$merge_with$merge2__5964 invoke "core.clj" 3066]
  [clojure.lang.ArrayChunk reduce "ArrayChunk.java" 58]
  [clojure.core$reduce1 invokeStatic "core.clj" 942]
  [clojure.core$reduce1 invokeStatic "core.clj" 934]
  [clojure.core$merge_with invokeStatic "core.clj" 3059]
  [clojure.core$merge_with doInvoke "core.clj" 3051]
  [clojure.lang.RestFn applyTo "RestFn.java" 139]
  [clojure.core$apply invokeStatic "core.clj" 667]
  [clojure.core$apply invoke "core.clj" 660]
  [user$eval61811$fn__61813 invoke "scratch.clj" 295]
  [clojure.core$keep_indexed$keepi_8568$fn_8569 invoke "core.clj" 7378]
  [clojure.lang.LazySeq sval "LazySeq.java" 42]
  [clojure.lang.LazySeq seq "LazySeq.java" 58]
  [clojure.lang.RT seq "RT.java" 535]
  [clojure.core$seq__5402 invokeStatic "core.clj" 137]
  [clojure.core$print_sequential invokeStatic "core_print.clj" 53]
  [clojure.core$fn__7310 invokeStatic "core_print.clj" 174]
  [clojure.core$fn__7310 invoke "core_print.clj" 174]
  [clojure.lang.MultiFn invoke "MultiFn.java" 234]
  [clojure.core$pr_on invokeStatic "core.clj" 3674]
  [clojure.core$pr invokeStatic "core.clj" 3677]
  [clojure.core$pr invoke "core.clj" 3677]
  [clojure.lang.AFn applyToHelper "AFn.java" 154]
  [clojure.lang.RestFn applyTo "RestFn.java" 132]
  [clojure.core$apply invokeStatic "core.clj" 665]
  [clojure.core$prn invokeStatic "core.clj" 3714]
  [clojure.core$prn doInvoke "core.clj" 3714]
  [clojure.lang.RestFn invoke "RestFn.java" 408]
  [clojure.main$repl$read_eval_print__9086 invoke "main.clj" 442]
  [clojure.main$repl$fn__9095 invoke "main.clj" 458]
  [clojure.main$repl invokeStatic "main.clj" 458]
  [clojure.main$repl_opt invokeStatic "main.clj" 522]
  [clojure.main$repl_opt invoke "main.clj" 518]
  [clojure.main$main invokeStatic "main.clj" 664]
  [clojure.main$main doInvoke "main.clj" 616]
  [clojure.lang.RestFn applyTo "RestFn.java" 137]
  [clojure.lang.Var applyTo "Var.java" 705]
  [clojure.main main "main.java" 40]]}

p-himik12:02:19

In px-tran-netter-ex, you're basically doing something like (conj #{:ax-m01} #{:ax-m01}). If that's indeed what you want (having a set of sets of sets of sets etc), it should work fine where the first set is a hash set. But somewhere along the way, you use a tree set. And that's where it fails.

p-himik12:02:50

A tree set tries to compare its items between each other. And sets cannot be compared, so a set cannot be a member of a tree set.

KJO12:02:48

Thanks. The px-tran-netter-ex is a big reduction of the original. I wanted to reduce the problem to its simplest manifestation. (There's further processing that runs some checks/reductions on the sets of sets). I'll have to go looking for the tree set. I reckon it's got to be in the `for' somewhere. Let me do some digging.

p-himik12:02:53

Just to make sure - are you sure you want to conj all those sets under the :src-ax-id key and not just combine them all with into or something like that?

p-himik12:02:33

Also, if you often find yourself processing deeply nested data structures, https://github.com/redplanetlabs/specter should make your life much easier.

KJO12:02:40

Unfortunately, I do need the excessive conj'ing. There are downstream checks regarding whether the final set members are similar enough to be acceptable. I use specter in other places. I like it.

👍 4
KJO14:02:57

Just closing this out. There was a PersistentTreeSet way down in the values and a conj was complaining. However, is it just me, or is the behavior of HashSet and TreeSet a little bit inconsistent?

KJO14:02:05

For example:

KJO14:02:49

;; returns #{nil #{:a} :a} as clojure.lang.PersistentHashSet
(conj
  (clojure.lang.PersistentHashSet/create (list nil :a))
  (clojure.lang.PersistentHashSet/create (list :a)))

;; throws clojure.lang.PersistentTreeSet cannot be cast to class java.lang.Comparable
(conj
  (clojure.lang.PersistentTreeSet/create (list nil :a))
  (clojure.lang.PersistentTreeSet/create (list :a)))

;; returns #{nil :a #{:a}} as clojure.lang.PersistentTreeSet
(letfn
  [(Cfn [v1 v2](if (= v1 v2) 0 1))]
  (conj
    (clojure.lang.PersistentTreeSet/create
      Cfn (list nil :a))
    (clojure.lang.PersistentTreeSet/create
      Cfn (list :a))))

;; returns #{nil #{:a} :a} as clojure.lang.PersistentHashSet
(letfn
  [(Cfn [v1 v2](if (= v1 v2) 0 1))]
  (into
    #{}
    (conj
      (clojure.lang.PersistentTreeSet/create
        Cfn (list nil :a))
      (clojure.lang.PersistentTreeSet/create
        Cfn (list :a)))))

;; returns #{nil #{:a} :a} as clojure.lang.PersistentHashSet
(letfn
  [(Cfn [v1 v2](if (= v1 v2) 0 1))]
  (clojure.lang.PersistentHashSet/create
    (seq
      (conj
        (clojure.lang.PersistentTreeSet/create
          Cfn (list nil :a))
        (clojure.lang.PersistentTreeSet/create
          Cfn (list :a))))))
All seems sensible if I supply a comparator that essentially returns 'same' or 'different'

KJO15:02:26

And a different `pr-str' representation to differentiate the two. I wonder is there a reason there isn't. Doesn't that break the pr-str / read-string 'loop'.

p-himik15:02:46

> is the behavior of HashSet and TreeSet a little bit inconsistent? Their behavior should not be consistent - they are different data structures. They are consistent w.r.t. operations on sets. > a different `pr-str' representation It is expected as well. > Doesn't that break the pr-str / read-string 'loop'. The loop has never been not broken. You can print lots of stuff that's not possible to read at all.

KJO15:02:27

good point. 😀

KJO15:02:25

But, it does seem odd that by defining a yes/no comparator rather that the equal/less/more one it allows the conj operation to proceed. Isn't the comparator properly part of the type?

p-himik15:02:10

Data types have a domain of definition. By tweaking a type, you're also tweaking its domain of definition.

KJO15:02:32

I don't know a great deal about type systems I'm afraid (that's one of the reason I love Clojure). I'll have to think about this a bit, and the probably a bit more. I don't know why, but it just makes me feel "uneasy". Thanks for the help. Much appreciated.

p-himik15:02:21

No problem. Another example - primitive arrays. An array is an array. However, once you create an array of a specific primitive type, you cannot put anything in there but the values of that type.

KJO15:02:58

I think my unease comes down to this

(conj
  (clojure.lang.PersistentTreeSet/create
    (fn[v1 v2](if (= v1 v2) 0 1))
    (list nil :a))
  (clojure.lang.PersistentTreeSet/create compare (list :a)))
To my amateur eye, the first treeset and the second are intuitively different types. They `compare' differently. Whereas, if I use the default comparator, I'd say they were the same type, but the conj blows up. If I think about your primitive array example, it's like the integer I'd like to add would be using different `math'. I suppose if I look at conj as 'join these things as best you can' rather than a straight append, it's a little easier for me to grasp. I might just replace the conj using the treeset with a conj-with-compare function, so I remember when I see it next time. (And I will forget).

KJO15:02:27

BTW: do you know the best way to cast a tree set to a has set? Or is my seq with create the way to go?

KJO15:02:44

hash set (typo)

p-himik17:02:02

You can create tree sets more easily with sorted-set and sorted-set-by. Probably the easiest way to convert any sort of collection to a hash set is to (into #{} coll).

p-himik17:02:34

Or (apply hash-set coll).

cddr12:02:31

Continuing my type hinting journey, I have some code that produces one of these.... https://kafka.apache.org/21/javadoc/org/apache/kafka/clients/producer/ProducerRecord.html I'm getting a warning that the constructor can't be resolved. This is due to the key and value parameters not being hinted. These are parameterized types <K>, and <V>. Is it actually possible to provide a hint that addresses these warnings?

noisesmith17:02:31

I doubt that this is because of K and V, because generics literally only exist in javac, and are only present as metadata by the time you are emitting clojure bytecode

noisesmith17:02:17

there is evil/bad code that inspects the metadata of generics, but that error wouldn't happen here, via the clojure compiler

noisesmith17:02:36

Looking at the constructors, they all take a K and V, I think hinting the other (statically known per constructor) args would suffice to eliminate this warning

noisesmith17:02:11

because the root issue here is that clojure's compiler wants to pick which constructor method to emit bytecode for, knowing K and V doesn't actually help there

cddr21:02:27

Ah cool. I’ll try just adding hints for the known types then. Thanks 🙏

zilti12:02:36

...uhm, a loop isn't a lazy construct, is it?

andy.fingerhut12:02:09

loop eagerly performs iteration. It does not return any value at all until the last iteration is performed. It might access the value of lazy sequences in its code.

pinkfrog13:02:55

for macro expansion, is the behavior of the clojure compiler similar to clojure.walk/macroexpand-all instead of clojure.core.macroexpand ?

zilti14:02:19

Good, that's a relief... I had a bug that was solved by adding a Timbre info statement accessing the data. Luckily it stayed resolved even after removing that again. Some days I question my sanity.

ambrosebs14:02:59

@zilti (thanks for covering for me @alexmiller) sorry about the docs, but simply core.typed is in heavy development to catch up with my years of research I did for my phd. don't use it for anything yet. but wish me luck!

🤞 4
ambrosebs14:02:05

I'm considering core.typed dead, but I'm reviving it in a new project called core.typed xD I haven't heard anyone seriously using core.typed for several years

ambrosebs14:02:13

or even not seriously

ambrosebs14:02:47

and there's no reason it shouldn't be dead. I got a phd on all it's shortcomings.

jeroenvandijk14:02:21

Do you want to share where it is heading or is that too early?

ambrosebs14:02:23

I'll make this clearer in the readme. Did you manage to find your way to the clojure/core.typed readme? that's the main one now

ambrosebs14:02:19

1. custom typing rules for macros. basically you write something that looks like a macro, but takes a (partially expanded) tools.analyzer-like AST and an expected type, and returns an AST with a type attached. you have complete control/responsibility for type checking/macroexpansion. working on first getting a feel for this system by porting all the special rules I already have for various internal forms to this system, then I'll figure out a decent abstraction for users to define their own typing rules.

jeroenvandijk14:02:44

Sounds very powerful! I'm guessing you could do this on a form by form basis given a certain context (available vars etc). I would love to use htat

ambrosebs14:02:20

yes the extension point is the fully qualified symbol for the current macro being expanded.

ambrosebs14:02:50

some obvious ones that really need this: async/go, core/doseq, core/for, core/ns

zilti14:02:29

Good luck! 🙂 I am actually trying to use it for a small project right now, and it works, I only have to figure out the annotation "language" again

zilti14:02:12

And of course, tons of annotations are missing for namespaces like clojure.string

dpsutton14:02:25

@ambrosebs do you have funding channels that we can contribute to? This seems impossible without money from interested people

ambrosebs14:02:53

2. symbolic execution. basically there's a bunch of clojure idioms at are really hard to type check with vanilla "bidirectional type checking". like (`let [f #(inc %)] (f 1))` they just need a bit of massaging. some are much more interesting like (update m :a #(+ %1 %2) 2) for the small price of undecidable type checking (lol) we can add symbolic execution to the type checking algorithm to reduce these to things like (inc 1) and (assoc m :a (+ (get m :a) 2)

ambrosebs14:02:11

@dpsutton thanks for asking. definitely not impossible and it'll happen one way or another, but see here to support me https://www.patreon.com/ambrosebs

jeroenvandijk14:02:09

@ambrosebs Do you have something for companies? Something like opencollective?

ambrosebs14:02:20

hmm I do have an LLC fwiw... what's special about opencollective?

jeroenvandijk14:02:01

It's the only one I know that allows a company to give giftcards to their employees for them to spend

jeroenvandijk14:02:30

Patron is nice, but it relies on individuals and it's (more) cumbersome for companies

jeroenvandijk14:02:55

OpenCollective also has a nice system that gives exposure to companies. Who sponsored what

jeroenvandijk15:02:31

I'll make sure you get some funding on OpenCollective 🙂

jeroenvandijk15:02:58

I would like to give Giftcards to all employees and let them decide what and where to spend. This is possible via OpenCollective. It just needs a bit more interesting Clojure projects

jeroenvandijk15:02:12

It's still experimental, but boot-clj has it like this https://github.com/boot-clj/boot#sponsors

jeroenvandijk15:02:40

I don't know why Github Sponsors doesn't have something like this

ambrosebs14:02:34

I'm a driven man at this point xD

dpsutton14:02:35

exactly what i was looking for. i'm in for $5 a month. thanks!

Alex14:02:38

Has anyone used clojure with azure app insights telemetry API and can give me some insight on how to implement it?

pinkfrog15:02:03

why is this form: ::r/router valid ?

ambrosebs15:02:07

it auto-resolves the namespace of the keyword, uses [reitit.core :as r] at the top of the file

pinkfrog15:02:13

I see, thanks.

fabrao20:02:35

Hello all, any advice about in-memory database for clojure?

fabrao20:02:25

I don´t need to store it in disk, only for fast queries

Chris Lester20:02:32

H2, Redis, Datascript, etc.

Chris Lester20:02:56

RocksDB also fast

fabrao20:02:58

and about Datascript?

zilti20:02:00

Datascript is probably the least overhead of these

zilti20:02:20

H2 also allows you to store on-disk, and it uses SQL

zilti20:02:35

Datascript though you'd have to serialize to disk yourself

fabrao20:02:47

ok, I don´t need saving to disk

fabrao20:02:05

only control some app states

noisesmith20:02:58

if it's simple enough, you can just use a hash-map in an atom

jumpnbrownweasel21:02:11

Regular maps/sets/vectors also work as an in-memory db of course. I assume you decided not to use them because you need database-like queries?

fabrao21:02:09

@mark540 yes, but I´m thinking using it with dynamic queries by business needs

fabrao21:02:46

I´ll start as regular maps

fabrao21:02:05

thx 4 help

Chris Lester21:02:38

Datascript also has a more interesting query language. Datomic also has an in memory version if I remember correctly (since its work).

noisesmith21:02:08

IIRC datomic and tonsky's Datascript lib both implement versions of the db paradigm as described in the alice book http://webdam.inria.fr/Alice/ which is called "datalog"

noisesmith21:02:33

oh right, datalog is the query language, Datascript and Datomic both support versions of it https://en.wikipedia.org/wiki/Datalog

Chris Lester21:02:37

Yes, I personally like it quite a bit better than SQL. Its a non turing complete logic language subset that is guaranteed to complete and not go into infinite recursion. Unlike mini kanren :)

andy.fingerhut22:02:40

Do you happen to know if there are any proven results on "time complexity' of datalog? I can look for such results myself, but if you know of them. e.g. is it provable that every datalog query can be executed in at most polynomial time in the "size of the database", or "size of the query"?

andy.fingerhut22:02:29

Or a separate but maybe related question: There are regular languages, context-free languages, context-sensitive languages, and "the languages recognizable by Turing machines" (recursively enumerable might be a correct term, but I'm on thin ice there). Is there a similar kind of know sets of "query power", like "SQL queries", "datalog queries", "Turing machine queries", probably in that order of "everything an earlier one can do, a later one can also do, but the later one can do more, too"? If so, are there other well known "query powers" in between those?

Chris Lester05:02:18

I looked through some of the research on Datalog (there is quite a lot) but it's old enough that I think the proofs of that guarantee given a finite set will take more sleuthing. All the academic papers I read (lightly) started with that as a presumption or discussed other matters (such as boundedness). The largest difference between Datalog and SQL is that Datalog is recursive, if you remove that they should be equivalent (although SQL99 was inspired to attempt to add recursion). It's the recursion that gives it more expressive power ... and yes, I think you could use expressiveness as a metric there.

noisesmith21:02:06

it's really cool that the Alice book is officially free online

noisesmith21:02:33

I've only read parts of it, but learned a lot from what I read

Chris Lester21:02:35

Thx for the link, have not read that one.

Chris Lester21:02:55

Currently exploring logic programming by writing a state machine to handle media publishing ...so love finding new material in the domain to read through.

plins21:02:47

is there anything like a drop-while-last ? same semantics of a drop-while but from right to left

hiredman21:02:30

seqs must be traversed head to tail, and may be infinite, so there is no efficient way to do that

👍 12
cddr21:02:25

SQLite has an in-memory variant if you connect just using the string “:memory:”.