Fork me on GitHub

have a bit of a weird question. let’s say I have a string of some clojure code like "#(println %)" or "(fn [& args] (println args))" that I want to apply some arguments to at runtime. how do I go about converting that string representation of the fn to something invokable? calling read-string results in a cons/list, and I’m a little bit lost in the weeds trying to create something out of that string that implements IFn and is invokable


Sounds like you want eval


:face_palm: I dunno why I was overthinking stuff I read on page one of sicp but thanks for rubber ducking with me. appreciate the response


You may also want to check out

Rob Haisfield01:06:32

Why is there so much crossover between Ruby and Clojure programmers?


I like to think we're a rebellious and creative bunch!

👍 2
Jakub Šťastný01:06:40

@rob370 is there? Well, if that's the case, I'd very much like to know as well. I am (was) a Ruby dev, to me I wanted to work in a lisp (simplicity of syntax, macros) as well as with a functional language (immutability, not OOP-centered). Ruby uses FP style a lot, I don't think I've ever seen a for loop, everyone uses each/map/reduce etc, Ruby has symbols as well, the :key syntax is familiar etc, so I'd say the barrier of entry is lower than coming from say C# or even Python. Would love to hear other people's thoughts on this one!

Rob Haisfield04:06:18

Maybe part of it is the culture around DSLs?

👍 4

I don’t know Ruby much, but I think both languages are designed and advertised around a certain programmer mindset. Highly dynamic, expressive and respecting of the programmer. Contrast it to say Go, which is designed in a way so a two week beginner (that is already trained in programming otherwise) can pretty much jump into a piece of code and understand it. Very few, obvious and clean concepts that are a little bit like a refined version of the lowest common denominator of mainstream languages.

👍 2

Ruby and Clojure both came to prominence in the 00s (I appreciate Ruby was around before then but it really took off when Rails came along), at a time when I dare say most developers were using languages that didn’t support creating powerful abstractions. I certainly was. I remember reading various Paul Graham essays around about the time, especially Beating The Averages, and hearing that there were more powerful languages out there, languages that had features that a Java programmer like me couldn’t even conceive of, that would make me much more productive than I could imagine. I ended up learning Common Lisp, then Python (sadly I never learned much Ruby at the time) and finally Clojure. I concede that Mr Graham had a point. Funnily enough, he didn’t mention Ruby but I put that language in the same category. Things like metaprogramming and functional programming are amazing and it’s easy to forget what life was like before languages like Ruby and Clojure became well known, and both languages allow you to do things that simply couldn’t be done with the languages that were popular back then.

👍 4

Ruby? You mean MatzLisp? Yep, it has a place. In, I believe, Mr Hickey's 10th anniversary ClojureCon talk, he called out Smalltalk and Common Lisp as his inspirations...maybe he was on the trail already when Rails popped Ruby onto everyone's radar?


I’m building a Clojure wrapper for web3j (blockchain crap) and basically I’m trying to take a JSON description of a contract’s interface and turn it into a bunch of convenience methods for calling those functions. web3j does this with an external codegen tool that generates class definitions for the methods. Of course my first thought was to use macros to do basically the same thing. However, having not done this level of work in Clojure before, I’m struggling with the proper way to implement it.


If I want to mirror the way web3j generates its classes, my current thinking is I’d make a macro that defprotocols the contract’s interface, and another that proxys a subclass of the web3j Contract class + implements my generated protocol. That seems like the best approach, and gets me basically the same result as if I ran web3j’s codegen.


(the Contract class is an abstract so I would have to use proxy)


Just trying to decide if this actually results in a more ergonomic experience than something else that defines some map of free functions based on the interface, then calling those against proxies. Of course, the Contract constructor is meant to result in a convenient experience from Java, so you inject a bunch of other dependencies too.


so I think I’m sort of talking myself into using the protocol based mechanism, because it’d only create one instance of the contract per, well, instance of the contract.


Should I still use proxy, or is deftype workable?


kind of looking like since it’s an abstract base class I have to use proxy or reimplement everything the abstract base does.


@anisoptera kindof a dumb question, but what is wrong with the Java lib?


I don’t want to have to generate the class definitions ahead of time


I want a library that works like ethersjs, which for context allows one to say a partial interface definition (i.e. one function) and then be able to call that function right away in that session


There’s a lot the Java lib does right and that’s why I’m not reimplementing it


I am just fixing this one thing so I can use it conveniently from Clojure without having to generate .class files ahead of time


(I mean, the other problem is that it generates Java code, not Clojure code)


i mean, I would personally bite the build system bullet and do it AOT - but


can you share some example json files


and roughly what they map to in the generated java?


the java generation is mainly to generate function signatures that take the proper input params / give the right outputs


so like here is kind of an example of what I did in clojure to handle single value returning view functions


(def contract
  (proxy [org.web3j.tx.Contract]
      [org.web3j.tx.Contract/BIN_NOT_PROVIDED       ;; contractBinary
       "0xB44825cF0d8D4dD552f2434056c41582415AaAa1" ;; contractAddress
       web3j ;; web3j
       credentials ;; credentials
       gasProvider ;; gasProvider
    (test [b]
      (str b))))

(defn make-read-contract-call-single-return [name input-types output-types]
  (fn [contract & args]
    (let [function (org.web3j.abi.FunctionEncoder/makeFunction
                    name input-types args output-types)]
      (.getValue (.executeCallSingleValueReturn contract function)))))


oh ignore the test fn at the end


so the compiler would have generated a subclass of org.web3j.tx.Contract and would have generated a static function which essentially does what the returned fn from make-read-contract-call-single-return does


the next step is generating a bunch of calls to that, and binding them to names


and ideally, even getting rid of the variadic part


er not static, member


dumb solution, might not be what you want, but you can always make a "invoke-contract-function"


yeah, that’s where I’m at right now


I have that, my next step is generating convenience methods basically


okay so assuming I had that abi contract loaded in


It’s kind of messy to have to define the list of input types and output types every time you want to call anything


I want to be able to do that


but also to have convenience


wait, why do you have do define your input and output types?


(defn balanceOf [contract addr]
  ((make-read-contract-call "balanceOf" ["address"] ["uint256"]) contract addr))


it’s an ABI


okay so what I mean is


so the system has to know what input/output types are being encoded and decoded into the call data


this balanceOf function is an example of what I would have to write for every unique function call I wanted to make


so that in the end i can write (balanceOf contract "0x00000…")


I’m already there, with manual entry of the final convenience function, but I don’t wanna do this manually when I could just generate


basically I guess I’m just looking for what the most clojure-y way would be to have a convenient interface to this stuff, if we ignored that web3j was underpinning it


(def ex-contract
        "constant": true,
        "inputs": [],
        "name": "totalSupply",
        "outputs": [
                "name": "",
                "type": "uint256"
        "payable": false,
        "stateMutability": "view",
        "type": "function"

(defn invoke-read
  [contract name input]
  (loop [methods contract]
    (if (= (get (first methods) "name") name)
      ((make-read-contract-all name 
                               (mapv #(get % "type")
                                     (get (first methods) "input"))
                               (mapv #(get % "type")
                                     (get (first methods) "output")) input)))


okay treat that as pseudocode


you could now do


(invoke-read ex-contract "totalSupply" {:_from ... :_to ... :_value ...})


I mean we still need an address and everything but I like this yeah


and then you would expect a map out with the return values


with an arg map even


like my code above doesn't do that


right i get what you’re going for


but thats where i was going with it


we’re just .. yeah


just make sure to insert all the warnings you have to


and potentially validate/restructure your contracts in memory


like maybe have a namespace that de-facto defines your "contract" type


and might store these all keyed under the method names


to avoid the linear scan or wtvr


see this is genius, here i am thinking i have to mangle the data into some format that’s acceptable and you’re like no, just use the abi as your interface directly


and yeah needs caching or w/e


but crypto burns a rainforest per call anyways so who cares


Wow, whatever happened to understatement? 🙂


but it feels a lot better than what i was doing which felt like writing java with parentheses


lol read calls are super cheap


it’s the write calls that cost a bunch


and i’m on a network that doesn’t boil the oceans, but yes, in general we’re not worried about high perf on a lot of these calls


and if i become concerned with perf, i can revisit the build step


yeah - and you might need to attach your web3j client to it in some way


the technique i've used for stuff like that is to return either a named map with defrecord


or a map with namespaced keys that hold the stateful bits


(defn create-contract [web3j-client contract-spec]
  (validate! contract-spec)
  {::web3j-client web3j-client
   ::contract-spec contract-spec})

(defn do-thing [contract]
  (.someMethod (::web3j-client contract) ...))

❤️ 2

absent other context you can use namespaced keys as a kind of private


ah that makes sense


not using *web3j-client* or something silly like that


which is usually what i did


that works too, i just like this better when i'm writing from scratch


personal preference


yep makes sense. thanks a ton 🙂