how to eval a cond case and return function object? - hy

got TypeError: Don't know how to wrap <class 'function'>: <function test_cond_list_fn.<locals>.<lambda> at 0x000001B879FD3D08>
when run
;a fn object
(setv a_fn (fn [x] (+ 1 x)))
;a mock predicator
(setv predicator True)
;inject predicator and a_fn into a (cond ..)
(setv cond_expr `(cond [(~predicator) [~a_fn]]))
;eval at another place
(eval cond_expr)
How to create the "cond_expr" so that get the result [a_fn] ?

In order to eval a HyExpression it must first be compiled to Python ast. While you're allowed to put arbitrary objects into a HyExpression, that does not mean you can compile it. (There has been a little talk of simulating this feature, but it's not available currently.)
The Hy compiler can only do this for a certain set of data types called Hy Model types, or for a few other types that can be automatically converted to these Hy Models.
There's no obvious way to represent a function object in Python ast, so there is no Hy Model for it. But you can compile a function definition.
=> (setv a-fn '(fn [x] (+ 1 x)))
=> (setv cond-expr `(cond [True ~a-fn]))
=> (eval cond-expr)
<function <lambda> at 0x0000020635231598>
Or a function's symbol.
=> (defn b-fn [x] (- x 1))
=> (setv cond-expr2 `(cond [True b-fn]))
=> (eval cond-expr)
<function <lambda> at 0x0000020635208378>

Related

Locally rebinding `+`

How to translate this Clojure code to Hy, so it prints 2?
It doesn't need to be like Clojure, i just want to hide + and replace it with - in local environment.
(defmacro q [expr]
`(let ~'[+ (fn [x y] (- x y))]
~expr))
(print (q (+ 3 1)))
In Clojure it prints 2 (let creates a local environment).
In Hy it prints 4.
How to make Hy print 2 also, by replacing the + with - ?
I need those local environments because i am making a DSL.
This doesn't do what you expect in Hy because + is a macro, and macro calls take precedence over function calls:
(defmacro x [] 1)
(defn x [] 2)
(print (x)) ; => 1
Your options are:
Instead of +, use a name doesn't have the same name as a core macro, like my+ or +2.
Only use your new + in contexts other than the head of an Expression (which is the only place Hy expands macro calls), such as (map + (range 10)).
In q, replace the symbol + in the input instead of just setting the variable +, as in something like
(defmacro q [expr]
(import hyrule [coll?])
(defn f [x]
(cond
(= x '+) '-
(coll? x) ((type x) (map f x))
True x))
(f expr))
(print (q (+ 3 1)))
Use defmacro to define a new macro named +. This is a bad idea because you lose access to the original + in this module, including in the expansions of macros you didn't write that expect + to have its usual meaning. Local macros are not yet implemented (#900).

"Ran into a RPAREN where it wasn't expected" when using "->>"

I have the following code:
(require [hyrule [-> ->>]])
(defn static/freezer [value freezer]
(cond [(not value) (setv freezer [])]
[(isinstance value list)
(do (if (not (isinstance freezer list)) (setv freezer []))
(.extend freezer value)
(setv freezer (->> (lfor i
(lfor j freezer :if j j)
(if (isinstance i list) i [i]))
(list)
(chain #*))))]
[True (raise (TypeError f"Sorry! The 'm/freezer' can only accept lists or non-truthy values!"))])
(return freezer))
(print (static/freezer [[1 2] [3 4] 5))
... but am getting the following error:
Traceback (most recent call last):
File "/usr/lib/python3.9/runpy.py", line 267, in run_path
code, fname = _get_code_from_file(run_name, path_name)
File "/home/shadowrylander/bakery/test.hy", line 12
(chain #*))))]
^
hy.lex.exceptions.LexException: Ran into a RPAREN where it wasn't expected.
I am assuming the ->> macro isn't taking effect, as every bracket checks out, but neither eval-when-compile nor eval-after-compile helps.
As suggested by the error message, (chain #*) is not lexically legal. #*, as well as #**, must be followed by a form. The underlying idea is that #* and #**, like ( and ", are not forms themselves, but characters that can be used along with some other characters to construct forms, namely (unpack-iterable …) and (unpack-mapping …). #1730 discusses some related issues. At any rate, lexing happens before any macros are expanded, so ->> can't get around this.

`self` can not use as arguments of a hy macro

The following macro tries to assign a member variable from init argument.
But
name 'self' is not defined
(defmacro optional_assign [x &optional [base self]]
`(lif ~x (setv (. ~base ~x) ~x) (setv (. ~base ~x ) None) ))
(defclass clsa []
(defn __init__ [self &optional y]
(optional_assign y)
))
(setv insa1 (clsa 123))
(print insa1.y) ;;=>123
(setv insa2 (clsa))
(print insa2.y) ;;=>None
The default argument is evaluated like an ordinary expression, so you want [base 'self], not [base self].
Also, you're missing a ~ for the first mention of x in the body.

Is there a way in the hy language to use doto on self?

hopefully someone can help me with this hy question. I am porting some python code over to hy, and was trying to figure out how I could remove some repetitive code using the doto macro. For example, look at a python class like this:
class Foo(object):
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
How could I convert this in hy to use doto?
(defclass Foo [object]
[[__init__ (fn [self x y z]
(doto self ;
(setv ...) ; What goes here?
))]])
The problem is that it looks like you normally do something like this:
(defclass Foo [object]
[[__init__ (fn [self x y z]
(setv self.x x)
(setv self.y y)
(setv self.z z))]])
I don't see a way of using (doto) on self.
That is an interesting idea. You can do this:
(doto self
(setattr "x" x)
(setattr "y" y)
(setattr "z" z))
But it's not much better. Consider defining a macro:
(defmacro vars-to-attrs [obj &rest attrs]
(let [[actions (list (map
(fn (a) `(setattr (str '~a) ~a))
attrs))]]
`(doto ~obj ~#actions)))
And then calling it like this:
(vars-to-attrs self x y z)
This might work better as a function though:
(defun vars-to-attrs-fun [obj &rest attrs]
(for [a attrs]
(setattr obj a (get (locals) a))))
And then call it like:
(vars-to-attrs-fun self 'x 'y 'z)
or, equivalent:
(vars-to-attrs-fun self "x" "y" "z")
If you just want to keep __init__'s locals, the easiest way is to directly .update the instance vars with the local vars.
(defclass Foo [object]
(defn __init__ [self x y z]
(.update (vars self) (vars))))
(By the way, the above is using our new defclass syntax from the version of Hy on Github, which won't work with the current PyPI version. [Update: it's now in the current PyPI release])
This does include all the locals, so you get a self.self, which is probably harmless, but you can del it after if you want. Hy sometimes generates locals to make statements act like expressions. These could also end up in the instance dict if you're not careful. You can avoid this by associng only the names you want:
(assoc (vars self)
'x x
'y y
'z z))
The new setv syntax also takes an arbitrary number of pairs, so you could do something like this instead:
;; new setv syntax
(setv self.x x
self.y y
self.z z)
You could pretty much do this before using tuples:
;; works in both Hy versions
(setv (, self.x self.y self.z)
(, x y z))
You can also avoid duplication in .update with a dict-comp, though this isn't usually shorter.
(.update (vars self) (dict-comp k (get (vars) k) [k '[x y z]]))
If you're still set on using doto, the correct syntax is:
(doto self
(-> (. x) (setv x))
(-> (. y) (setv y))
(-> (. z) (setv z)))
This does avoid repeating self, but it's not shorter than the aforementioned alternatives, so doto is the wrong tool for this particular job.
Update
I've made an issue for this https://github.com/hylang/hy/issues/1532
We might be adding an attach macro to Hy. I also posted an implementation if you want to try it out early.
Usage:
(defclass Foo []
(defn __init__[self x y z]
(attach self x y z)))
Since the attachment target is the first argument, attach would also work in a -> or in a doto, e.g.
(doto self
(.configure foo bar)
(attach spam eggs))

Parsing of nested array structure

I'm trying to parse a nested array structure of the following form:
[element [[child1] [child2] [child3 [[subchild1] [subchild2]]]]]
I would also like to return a list with all symbols (and nothing else), regardless of nesting depth; however, I'm not looking for flatmap or flatten etc, since I need to perform more complicated additional work on every element.
This is what I came up with so far:
(defn create-element [rs element]
(if (symbol? element)
(cons element rs)
rs))
(defn parse
([rs element] (create-element rs element))
([rs element [children & more]] (if (nil? more)
(parse (parse rs element) (first children))
(parse (parse rs element) (first children) more))))
(defn n-parse [element]
(apply parse () element))
This works fine for the following input:
=> (n-parse ['bla [['asd] ['kkk] ['sss]]])
(sss kkk asd bla)
But this doesn't work:
=> (n-parse ['bla [['asd] ['kkk [['ooo]]] ['sss]]])
(sss kkk asd bla)
I'm still trying to wrap around my head around the types but can't seem to manage to get it right. For example, Haskell makes this easy with pattern matching etc, whereas Clojure doesn't allow same arity function overloading.
Also is there a more concise / idiomatic way (without having to resort to if?) I'd prefer pure Clojure solutions (no external libs) since this is actually for a Clojurescipt project.
Many thanks for any help!
I don't see whats wrong with flatten. If you want to do some work on the items first, do the work first and then flatten the result:
(defn map-tree
"Example: (map-tree + [1 2 [3 5]] [3 4 [5 6]])"
[f & trees]
(if (every? coll? trees)
(apply map (partial map-tree f) trees)
(apply f trees)))
(defmulti transformator identity)
;; transform 'sss element into something special
(defmethod transformator 'sss [_] "sss")
;; default transformation
(defmethod transformator :default [v] v)
Test:
user> (flatten (map-tree transformator '[bla [[asd] [kkk] [sss]]]))
(bla asd kkk "sss")
user>
Would that not work?

Resources