If you use map on a string, map expects the function in its first argument to return a character. It builds a new string from the characters returned by the function.
arc> (map (fn (x) #\a) "hello")
"aaaaa"
And, what would be a simple way to do what I'm trying ?
I don't know, what are you trying to do? If you'd like to have the result be a concatenated string, this may not qualify as "simple"... but...
Now, whenever something of the wrong type causes an error in Arc, that's an opportunity to have Arc do something useful instead, if there's something we'd like and would make sense. Thus we could say, if we wanted to, that if we pass a string to map and the function returns a string, paste that returned string into the result. That could be useful. Suppose, for example, we want to escape HTML:
It deals with lists and strings well, which is decent: Arc's only other sequence-like type is the table (I don't think you'd ever want to treat symbols as a sequence of 1-character symbols; you'd just use a string). Tables would work better if they were properly coerced, cf. the comment above tablist and listtab in arc.arc.
The more I think about it, the more I like this model. Conceptually, it seems that map should behave like
(def map (f seq)
(map-as (type seq) f seq))
even if it's not implemented like that -- all the coercions would surely be slow. (Tangential: map would also need to handle multiple sequences.) But it makes more sense for map and coerce to at least have compatible behavior. Plus, map's current behavior is a degenerate case of the coerce-compatible map:
I've spent some time thinking about how to extend it for multiple-dispatch, and I didn't want to also think about setting the arg index to dispatch on.
Also, I wonder, if there is a way to replace pairs of characters? I mean, if we need a code, that would replace 'number two number' with 'one two three,' should we use (multi)subst for it or is it done the other way (regexps etc)?
(def mapstr (f . seqs)
(string (accum a (for i 0 (- (apply min (map1 len seqs)) 1)
(a (string (apply f (map1 [_ i] seqs))))))))
arc> (mapstr [string "." _] "test")
".t.e.s.t"
And we can plug that into the definition of map:
(def map (f . seqs)
(if (some [isa _ 'string] seqs)
(apply mapstr f seqs)
;; the rest is the same as the arc3.1 def
(no (cdr seqs))
(map1 f (car seqs))
((afn (seqs)
(if (some no seqs)
nil
(cons (apply f (map1 car seqs))
(self (map1 cdr seqs)))))
seqs)))
arc> (map [string "." _] "test")
".t.e.s.t"
Though you know, it's funny, my brain doesn't actually like it that map returns different things depending in the types of its arguments. In the same way that a long run-on sentence is annoying if I can't tell what's it's saying until the end, if I see a map I don't know if it's returning a list or a string or whatever until I've looked at its arguments and seen what they are.
So I might like map to always return a list, but still have it treat a string as if it were a list of characters. The example then becomes:
When you map across a string, it coerces the whole result into a new string character-by-character. It's like if you (hypothetically) weren't allowed to have nested lists but tried to do
(map [list 1 2 3 _] '(8 6 7 5 3 0 9))
Each string element is a character, not a whole string. Thus
arc> (= s (newstring 3))
"\u0000\u0000\u0000" ; these are null characters, by the way
arc> (= (s 0) #\a)
#\a
arc> s
"a\u0000\u0000"
arc> (= (s 1) #\b)
#\b
arc> s
"ab\u0000"
arc> (= (s 2) "foo")
Error: "string-set!: expects type <character> as 3rd argument, given: \"foo\"; other arguments were: \"ab\\u0000\" 2"