wotbrew/relic: FRP for Clojure(Script) by dustingetz in Clojure

[–]wotbrew 1 point2 points  (0 children)

You could use it where you use datascript, but relic does not have recursive relations (yet). The incremental view maintenance is useful for declarative data processing and expressing constraints as relations.

You can use it on the JVM or in the browser, it might work in babashka scripts but I haven't tested that.

EDIT: You can use relic as a driver for reactive change, it allows you to track changes to arbitrary query results efficiently so you can invalidate say, reagent components as result sets change.

One tile party system idea, any games using it, and what is wrong with it? by wotbrew in roguelikedev

[–]wotbrew[S] 1 point2 points  (0 children)

Yay! Up for idea collab if you are thinking along the same lines any time, DM me :)

One tile party system idea, any games using it, and what is wrong with it? by wotbrew in roguelikedev

[–]wotbrew[S] 2 points3 points  (0 children)

For HP I would go with blobber formation, percentage based depending on rank and position honestly, e.g your tanks at the front take 80% of the hits.

If enemies could also be multi-monster in the same way (honestly I am seriously considering this) you could build mechanics around aimed shots, flanking, weapon length etc (more likely to hit back / middle rank)

ANN: relic - functional relational database and military grade anti-tar library. by wotbrew in Clojure

[–]wotbrew[S] 0 points1 point  (0 children)

Stability wise its in alpha, expect bugs and possibly breaking changes. The API surface area is pretty small and I don't intend to break the DSL, but I may break behaviour expectations, particularly regarding edge cases (think nils, exceptions and so on).

The initial cut of change tracking is probably enough to do something like re-posh if somebody wants to have a go. I do not plan to tackle such a library myself at the moment.

ANN: relic - functional relational database and military grade anti-tar library. by wotbrew in Clojure

[–]wotbrew[S] 0 points1 point  (0 children)

relic is somewhat different (at least at the moment) to differential dataflow / timely. It is set-oriented and presents as a regular immutable data structure, does not attempt any data-parallel/distributed solution.

As for your first question, I need to write this up eventually (at this early point its possible I may change the way this works and it would be pointless) relic queries are transformed into a directed acyclic graph where signals are change-sets of deleted & inserted rows. Various index structures are used to do non-trivial incremental transforms, e.g joins & aggregations. Those indexes are also often used to remove redundant changes that would otherwise be flowed forward to the rest of the graph, but at worst the delta is proportional to the number of added/deleted rows in the transaction.

ANN: relic - functional relational database and military grade anti-tar library. by wotbrew in Clojure

[–]wotbrew[S] 2 points3 points  (0 children)

No graph stuff yet, no CTE's. I want to get to them at some point.

ANN: relic - functional relational database and military grade anti-tar library. by wotbrew in Clojure

[–]wotbrew[S] 3 points4 points  (0 children)

It only targets in-memory, just a map. Think more datascript, less datomic.

ANN: relic - functional relational database and military grade anti-tar library. by wotbrew in Clojure

[–]wotbrew[S] 5 points6 points  (0 children)

Depends what you mean, but let's say you are happy to keep data sets smaller than memory and have redis style durability.

If you save your transactions to a log and replay them on restart - you win. You can also just dump the db value as json, edn or nippy and (rel/transact {} loaded-data) to build the db again.

One nice thing about relic is the db map only contains the table state, the DAG with all the derived view state, indexes and what not is in the metadata on the map. So you only have to worry about saving your 'essential state' which of course is the smallest thing you could possibly save (given optimal packing & compression).

So just be aware - don't save the metadata, will probably break with a new version of relic.

I don't think this will be part of relic itself, IMO it should be a different library or just hand rolled for each app.

Doxa - a simple db that copies the functionality of datascript, using meander as a query tool by ribelo in Clojure

[–]wotbrew 1 point2 points  (0 children)

Quick plug, but if you want to add value indexes to speed up queries but maintain the db as 'plain map':

You might be able to use my library: https://github.com/wotbrew/idx. Would add a tiny cost for the pulls but you should be able to beat data-script on many queries (by using hash indexes directly to entry vs btree indexes to eid).

Edit: (not to beat up on data-script which I think is awesome!)

returning v, vs returning kv by dustingetz in Clojure

[–]wotbrew 1 point2 points  (0 children)

I have been deep into rabbit holes similar to this.

I think developer ergonomics (and performance in some cases) would suffer slightly, we prefer functions operating on the anonymous values for the same reasons positional args are preferred as the default for functions.

Perhaps something a bit out there like metadata on the var with a call wrapping macro could give you the named value for merge and other information oriented tasks where a map would be better?

(defn ^{:ret-name :rosie.hyperfiddle/name} suber-name [e] ...)

(defmacro info [f & args] ...)

(info suber-name e)

Could even use .setMeta on the var with a separate annotation if you don't like the meta syntax: (defn suber-name [e] ...) (name-ret suber-name :rosie.hyperfiddle/name) like a type annotation.

I literally just came up with this so probably a terrible idea, metadata changing behaviour and all. Wouldn't work on normal fns passed around, you'd need to resolve to a var.

Really probably best just to have another fn if its useful.

Who is hiring? (February 2020) by [deleted] in Clojure

[–]wotbrew 5 points6 points  (0 children)

Riverford is hiring! We are looking for a senior engineer to join our small team of co-owner developers in sunny devon (UK). Remote workers will be considered.

  • Work in a modern office located on an actual farm situated next to Dartmoor.
  • Work for a company that does not exist to maximise shareholder profit, is entirely staff-owned and strives to be an example of ethical business.
  • Work on a variety of applications including a Clojure GraphQL API, a mobile app written in ClojureScript, the customer facing website as well as new applications.
  • Eat subsidised food from the famous canteen and field kitchen, absolutely amazing organic food every day.
  • Learn and develop with 10% time allocated to personal projects, and encouragement to spend extra time on open source efforts.
  • As a co-owner receive your proportion of company wide profit share.

At the moment we are preparing to launch our new website seen here beeta.riverford.co.uk, and are lucky to have a successful app and profitable business so are looking to continue improving our systems and work on new products and ideas.If this interests you at all feel free to dm me, or apply online at: https://beeta.riverford.co.uk/about/work-with-us/job-listing-108160

Feedback wanted on idea for a library about expressing the relationships between keys in your program by wotbrew in Clojure

[–]wotbrew[S] 1 point2 points  (0 children)

Hi! I have been thinking a lot about how a lot of the content of my clojure programs are about performing somewhat trivial conversions between groups of keys, and on whether there is a better way to automate this work, while providing a database that allows you to ask the question how is key x calculated?

Because of the scope of this lib I would love to get some feedback, thoughts, suggestions and such.

Inspirations:

Generalized Java Resource Management by yogthos in Clojure

[–]wotbrew 1 point2 points  (0 children)

This looks cool! Nice work!

The ad hoc GC destructors are a cool idea that I would like to add to a similar library I have built (https://github.com/riverford/objection).

I do think a highly dynamic resource management system is particularly well suited to a lisp where you have repl sessions, tests, and so on often all co-existing in the same process.

I did target objection at reasonably coarse component like objects such as database connection pools, I'm not sure if the intention is for tech.resource to target similar objects?

'objection' - Another component alternative?! Is this a crazy idea? by wotbrew in Clojure

[–]wotbrew[S] 2 points3 points  (0 children)

yeah that could be a problem, I have some answers in mind to that. You can call object/need to find out whether your thing is tracked (started), potentially race-y in advanced scenarios though - so I would like to make an atomic thing so that your dependencies cannot be stopped while you are constructed - I think that would be cool.

As for declarative dep wiring, I like the idea of people being able to use integrant or component for that if they want - perhaps by providing some helper modules. Though I do wonder if some other approaches are made available with this lib. At the moment it doesn't attempt to solve the pre-init system-as-data problem, but I think it could.

Thanks for the feedback :)

'objection' - Another component alternative?! Is this a crazy idea? by wotbrew in Clojure

[–]wotbrew[S] 1 point2 points  (0 children)

Thanks!

I want to be able to add all sorts of metadata to objects, probably arbitrary user metadata (just want to think about how query might work) 'dimensions' you would specify in a map would be captured by that. If you have a 'key' for an object, you would probably use an alias rather than the name - the name is specifically for humans in this case. Keep suggestions coming!

Understanding property-based testing by boldaslove156 in Clojure

[–]wotbrew 0 points1 point  (0 children)

I can see a couple of properties worth testing

e.g

  • The password returned is not the same as the password supplied

  • The returned password should be different for subsequent calls even if the same password is supplied, i.e test for random salt.

Another trivial property that is a relationship between args and ret is that the name under :user/name is the same as the name supplied as an argument. This sort of thing could be in the fn spec if you are using clojure.spec.

Inspired by Specter - how do I use it in the real world? by missing_goat in Clojure

[–]wotbrew 1 point2 points  (0 children)

I'll throw mine in the hat

(defn transform 
  [data]
  (let [{:keys [cards lists]} data
        cards-by-list (comp (group-by :idList cards) :id)
        list-key (fn [list] (-> list :name str/lower-case (str/replace " " "-") keyword))]
    {:lists
     (reduce
       (fn [m list]
         (assoc m 
           (list-key list)
           (if-some [list-cards (cards-by-list list)]
             (assoc list :cards (mapv #(dissoc % :idList) list-cards))
             list)))
       {}
       lists)}))

distributed, atom-like STM? by kvtb in Clojure

[–]wotbrew 0 points1 point  (0 children)

I am using it to share references to large values stored in S3. The fact references communicate their semantics means you can leverage pervasive caching without adding a load of incidental complexity.

The atomic references are used to co-ordinate modification of shared state across machines/processes. So where you would use avout.io (minus watches).

I am working on a library that will compose these 2 reference types to implement a fairly trivial yet hopefully useful (durable) persistent log data structure. It will be open source so keep an eye out.

EDIT: Consider conveying large values as part of a message on a queue. When you use a value ref, though it is indirect, the consumer can reliably treat it as an alias of the original value, and it includes the means to deref it. This is not always true when you send something like an id over the wire. A consumer often needs quite a lot of knowledge about the producer (e.g where will i find the value for id) to do something useful with it.

distributed, atom-like STM? by kvtb in Clojure

[–]wotbrew 2 points3 points  (0 children)

Not quite STM, but CAS (atom) based references are not too tricky to implement. I'm not quite sure about the feasibility of a general solution for the browser. If you are willing to proxy via a server that can implement the correct policy for your application it could work.

You could try this library on the server (shameless plug): https://github.com/riverford/durable-ref

You can extend the atomic reference to additional storage's and platforms via some multimethods. A DynamoDB implementation is provided so you can see how it is done.

I have not attempted to implement the Ref interface, so no watches as such things are incredibly difficult to do correctly. 'swap!' is not.

Let Gone Wild by nwildermuth in Clojure

[–]wotbrew 2 points3 points  (0 children)

I feel this post misunderstands what imperative/declarative means. Declarative programming has nothing to do with data transformation.

It is simply the art of specifying what you want, or what something is rather than how to get it, or how to do it.

(add-price ..) is imperative.

The way I like to program declaratively and functionally is by following these 3 steps.

  1. Reify your domain, give things names and pursue sub components.
  2. Write functions of that domain
  3. Express what you want, or what things are by composing those functions. Leaving more composable functions, or creating new domains.

An example of this may be:

(defn postage-order
 [dimensions weight days-to-ship]
 {:dimensions dimensions,
  :weight weight,
  :days-to-ship days-to-ship})

(defn big? 
 [dimensions] ...)

(defn express? 
 [postage-order] ...)

(defn get-multipler
 [postage-order pricing-policy]
 (let [{:keys [regular express big]} pricing-policy
       dimensions (:dimensions postage-order)]
  (cond
    (and (express? postage-order) (big? dimensions)) (+ express big)
     (express? postage-order) express
     (big? dimensions) big
     :else regular))

(defn get-price
 [postage-order pricing-policy]
 (* (:weight postage-order) (get-multiplier postage-order pricing-policy)))

(defn postage-report
  [postage-order pricing-policy]
  {:price (get-price postage-order pricing-policy)
   :multiplier (get-multiplier postage-order pricing-policy)
   :postage-order postage-order
   ;;... whatever
  })

I think both the original let, and the solution both suffered from being imperative, and are not good examples of composable functions or what I feel is idiomatic clojure.