I’m trying to create a list of map elements which fit a particular condition. My code looks like this:
private function myDogs'(all' : list(string * dog), dest' : list(dog), key' : address) : list(dog) =
switch(all')
[] => dest'
[hd::tl] =>
if (hd[1].owner == key')
myDogs'(tl, dest' ++ [hd[1]], key')
else myDogs'(tl, dest', key')
entrypoint myDogs() : list(dog) =
myDogs'(Map.to_list(state.dogs), [], Call.caller)
Compiling fails with this error:
Error: Http request for https://compiler.aepps.com/compile failed with status code 403. Status: Forbidden. Error data: {"reason":"Parse errors\nline 16, column 47: Unexpected token '*'"}
It’s obviously complaining about this definition in the function’s arguments: all' : list(string * dog), dest'
. The reason why I wrote it like this is how the documentation defines the output of Map.to_list:
Map.to_list(m : map('k, 'v)) : list('k * 'v)
Obviously I can’t define my function in the same way. Hence I wonder how I should define my function. Also I wonder whether I’m doing things in the most efficient way. Is there perhaps a better way to get a list of filtered map elements?
*
is refering to new sophia syntax that will be released with the new VM in next hard fork. Look at the sophia reference for 4.X.X version of the node: protocol/sophia.md at aeternity-node-v4.2.0 · aeternity/protocol · GitHub
Also maybe a generic filter is better, have a look at: ae-sophia-functions/List.aes at master · thepiwo/ae-sophia-functions · GitHub
Thanks for the fast answer.
I followed your suggestion, the code is now:
private function filter(f : ('a) => bool, l : list('a)) = filter'(f, l, [])
private function filter'(f : ('a) => bool, l : list('a), acc : list('a)) =
switch(l)
[] => acc
e :: l' =>
if(f(e))
filter'(f, l', e :: acc)
else
filter'(f, l', acc)
entrypoint myDogs() : list(dog) =
filter((_ :: dog) : bool = dog.owner == Call.caller, Map.to_list(state.dogs))
The only thing which is still unclear to me is how to write the closure properly in the call to filter. I can’t find anything about closures in Sophia in the documentation.
For your initial example, this is what you meant I think:
record dog = { owner : address }
record state = { dogs : map(string, dog) }
entrypoint init() = { dogs = {} }
entrypoint myDogs() : list(dog) =
myDogs'(Map.to_list(state.dogs), [], Call.caller)
private function myDogs'(all' : list(string * dog), dest' : list(dog), key' : address) : list(dog) =
switch(all')
[] => dest'
(_, dog)::tl =>
if (dog.owner == key')
myDogs'(tl, dog :: dest', key')
else myDogs'(tl, dest', key')
Using List.filter
wont get you all the way since you have tuples (string * dog)
and you want just a list of dogs?! I.e. you need to do a projection as well.
1 Like
Thank you. Your code makes a lot of things clear to me. The only issue is that the current Sophia editor doesn’t support the definition with the * yet. Is there a way to reach the same effect under the current version of Sophia?
I though to use a tuple instead of the *, but that’s not working. The code is now:
private function myDogs'(all' : list((string, dog)), dest' : list(dog), key' : address) : list(dog) =
switch(all')
[] => dest'
(_, dog)::tl =>
if (dog.owner == key')
myDogs'(tl, dog :: dest', key')
else myDogs'(tl, dest', key')
entrypoint myDogs() : list(dog) =
myDogs'(Map.to_list(state.dogs), Call.caller)
resulting in the error: Error: Http request for https://compiler.aepps.com/compile failed with status code 403. Status: Forbidden. Error data: {"reason":"Type errors\nCannot unify list(dog)\n and address\nwhen checking the application at line 25, column 5 of\n myDogs' :\n (list((string, dog)), list(dog), address) => list(dog)\nto arguments\n Map.to_list(state.dogs) : list((string, DoggyChain.dog))\n Call.caller : address\n"}
The current compiler supports *
for tuples, but you can adjust if your tools doesn’t support it…
You are trying to call a three argument function with two arguments, strangely that doesn’t work… It should be myDogs'(Map.to_list(state.dogs), [], Call.caller)
on the last row - just like the compiler tries to tell you.
Damn. I completely looked over the fact that I simply forgot one of the arguments… Thanks for your help!