if I have a structure of data that has a bunch of attributes that are optional, what’s the best way to express this in the current meander epsilon?
Yes. The reason epsilon
and prior versions have never had m/or
on the right side was because I did not have a notion of “substitution failure”. In the near future, for rewriting, it will be legal to use m/or
on the right and relax restrictions on variables needing to be bound. If you use a variable on the right side which is was not bound then that will fail.
(m/rewrite [1]
[?x & (m/or [?y ?z] _)]
[?x (m/or ?y 2) (m/or ?z 3)])
;; =>
[1 2 3]
To me, this makes sense because, in practice, the time we actually care about a variable being unbound is when we need to “use” it.
And people sort of do this kind of thing all the time with nil
except the semantics of failure is rather ad-hoc.
So here we get something that feels both dynamic and static. Dynamic in the sense that we can build things we accept and return different shapes of data, and static in the sense that these constraints provide some very nice properties in exchange for giving up some control.
I’m inspired by Haskell and Peyton Jones: steadily growing the language to accept more and more programs without compromising on core value propositions.
sounds great, looking forward to it 👍
this is my example:
{:address {:zipCode ?zipcode
:city ?city
:stateAcronym ?state-abbr
:state ?state
:streetNumber ?street-number
:street ?street
:complement ?address-complement
:point {:lon ?lon
:lat ?lat}
:confidence ?address-confidence
:neighborhood ?neighborhood
:country ?country-abbr}}
right side:
{:listing/zipcode ?zipcode
:listing/city ?city
:listing/state-abbr ?state-abbr
:listing/state ?state
:listing/street-number ?street-number
:listing/street ?street
:listing/address-complement ?address-complement
:geo.point/lon ?lon
:geo.point/lat ?lat}
in my case, every attribute match there is optional, is this an applicable usage? and if so, how should I do it?
Without being too assumptive about your goals, I will say that for a situation like this, if you’re used to the destructure first ask questions later approach, then it’ll feel frustrating.
Probably the best tool for situations like this would be m/search
where each clause addresses a particular concern.
the goal is to extract the information I can find, if my assumption is right that meander is caring a lot about matching all, these would be the opposite, a more relaxed way to do “match whatever you can”, and them nullify everything else (that can’t be matched), makes sense?
Yes, for this situation, I recommend using m/search
where each clause can be as strict or as loose as you want.
I think I don’t understand m/search well enough, can you show me what it looks like? (with my address template for example)
Yeah. I’ll make a quick example but then I have to drop and take care of my kids. If my example doesn’t help, you can leave me some more requirements and I will try and reply in a couple hours. One second.
(from the docs I understand search returns many outputs, in my case I still want a single map on the output)
no worries, whenever you have the time, thanks 👍
I’m playing with search to try it out, this looks good:
(m/search {:a 1}
{:a ?a :b ?b :c ?c}
[?a ?b ?c])
=> ([1 nil nil])
but if the query goes a bit deeper like:
(m/search {:a 1}
{:a ?a :b [?b] :c ?c}
[?a ?b ?c])
=> nil
Then I got just nil
similar to matching nested maps, if the parent is there its ok (even if the map is only partially matched):
(m/search {:a 1 :c {:d "d"}}
{:a ?a :b ?b :c {:d ?d :e ?e}}
[?a ?b ?d ?e])
=> ([1 nil "d" nil])
but goes nil
if :c
is out:
(m/search {:a 1}
{:a ?a :b ?b :c {:d ?d :e ?e}}
[?a ?b ?d ?e])
=> nil
I can totally understand if my case is out of scope, but what I’m looking for is something that can just ignore everything that can’t be matched, and just return whatever was possible to match, hope these examples can help to understand the situation 🙏
Sorry, I keep getting interrupted by kids but here’s a silly example of what I meant.
(let [address {:zipCode "12345"}]
(into {:point? false
:zip-code? false}
(me/search address
{:point {:lon _, :lat _}}
[:coordinates? true]
{:zipCode (me/pred string? ?zip)}
[:zip-code? true])))
;; =>
{:point? false, :zip-code? true}
(I’m using me
as the my alias instead of m
).
But the idea is to search for what you’re looking and if you’re making a map then the right sides return pairs.I just got to this code here too 🙂
(m/search {:a 1 :b [3]}
{:a ?a} {:aa ?a}
{:b [?b]} {:bb ?b}
{:c {:d ?d :e ?e}} {:dd ?d :ee ?e})
cool, I think makes sense
thanks!
NP. This would probably be a good thing to stick in the cookbook.
You can use this approach in different ways, validation is one example where you can return a sequence of errors, etc.
Anyway, if you have more questions ask them here or in the main channel. Also, I’m keen to hear your frustrations as well. I’ve been working on the next version of the library and I’m trying to address all of the negative feedback, pain, and suffering I’ve gotten over the years. 🙂
no worries, I found meander one of the most amazing things to data transformation, really great job here!
I love the clarity of being able to look at the source and target structures in a simple way, and excited for the next gen that’s is coming 🙂
Me too! 😅 The support from folks in this channel has been inspiring.
I believe you saw you saying something about allowing (m/or)
in the right side for the next version, I guess with that I would be able to just put m/or
in each output option to solve this case, did I get it right?