"Adding" strings

42 views
Skip to first unread message

Peter Wolf

unread,
Feb 26, 2009, 11:11:39 AM2/26/09
to clo...@googlegroups.com
Hey all,

What is the idiomatic way to concatenate strings? Here are some things
that I expected to work, but didn't

(+ "foo" "bah")
(conj "foo" "bah")
(into "foo" "bah")

For the moment I am doing

(.concat "foo" "bah")

But it seems wrong

Thanks
P


Laurent PETIT

unread,
Feb 26, 2009, 11:14:35 AM2/26/09
to clo...@googlegroups.com
(str "foo" "bah")

and if you have a collection you can (apply str coll)

HTH,

--
Laurent

2009/2/26 Peter Wolf <opu...@gmail.com>

pmf

unread,
Feb 26, 2009, 11:15:17 AM2/26/09
to Clojure
On Feb 26, 5:11 pm, Peter Wolf <opus...@gmail.com> wrote:
> What is the idiomatic way to concatenate strings?  Here are some things
> that I expected to work, but didn't
>
>     (+ "foo" "bah")
>     (conj "foo" "bah")
>     (into "foo" "bah")
>
> For the moment I am doing
>
>     (.concat "foo" "bah")

(str "foo" "bah")

Shawn Hoover

unread,
Feb 26, 2009, 11:15:56 AM2/26/09
to clo...@googlegroups.com
Try (str "foo" "bah").

Peter Wolf

unread,
Feb 26, 2009, 2:01:19 PM2/26/09
to clo...@googlegroups.com
Thanks all.

I think appending a bunch of strings is a pretty common operation.

Is there any reason that str is limited to 2 arguments? It would be
nice to do (str "foo" "bar" "baz") --> "foobarbaz".

Is there a good reason that + can't do the right thing as with other
Java and scripting languages? I think this would be popular with
non-LISPers.

P



Laurent PETIT wrote:
> (str "foo" "bah")
>
> and if you have a collection you can (apply str coll)
>
> HTH,
>
> --
> Laurent
>
> 2009/2/26 Peter Wolf <opu...@gmail.com <mailto:opu...@gmail.com>>

Matt Revelle

unread,
Feb 26, 2009, 2:07:17 PM2/26/09
to clo...@googlegroups.com
On Feb 26, 2009, at 2:01 PM, Peter Wolf wrote:

>
> Thanks all.
>
> I think appending a bunch of strings is a pretty common operation.
>
> Is there any reason that str is limited to 2 arguments? It would be
> nice to do (str "foo" "bar" "baz") --> "foobarbaz".

It does. Try it out. =)

Perry Trolard

unread,
Feb 26, 2009, 2:13:49 PM2/26/09
to Clojure
> Is there any reason that str is limited to 2 arguments? It would be
> nice to do (str "foo" "bar" "baz") --> "foobarbaz".

Try it! (Hint: "With more than one arg, returns the concatenation of
the str values of the args.")

> Is there a good reason that + can't do the right thing as with other
> Java and scripting languages?  I think this would be popular with
> non-LISPers.

I think any desire for that disappears once you discover str, but the
implementation reason against it is that it'd require arg checks for
+, which you want to be fast. Look at the source for + & str --
they're both optimized for what they do.

Perry

Phil Hagelberg

unread,
Feb 26, 2009, 2:18:11 PM2/26/09
to clo...@googlegroups.com
Peter Wolf <opu...@gmail.com> writes:

> Is there a good reason that + can't do the right thing as with other
> Java and scripting languages? I think this would be popular with
> non-LISPers.

Putting a type check in + would slow down basic math, and there is a
class of user who will complain loudly if basic math slows
down. However, this also means that > and < also don't work on strings,
which is pretty lousy.

One approach that's been proposed in #clojure is to make these functions
more capable by default, but then provide a fast-math library that could
redefine them in terms of numerics-only. I'm a big fan of functions
doing the most helpful thing by default but being able to offer better
speed when you need it.

Convenience vs speed is always a trade-off, but I think convenience
should win in the default case. What do others think about this?

-Phil

opu...@gmail.com

unread,
Feb 26, 2009, 2:26:51 PM2/26/09
to clo...@googlegroups.com
Doh! *engage brain BEFORE emailing*

Sorry to be not thinking... Coffee now!

:-P

Sent via BlackBerry from T-Mobile

-----Original Message-----
From: Perry Trolard <tro...@gmail.com>

Date: Thu, 26 Feb 2009 11:13:49
To: Clojure<clo...@googlegroups.com>
Subject: Re: "Adding" strings

Cosmin Stejerean

unread,
Feb 26, 2009, 2:33:44 PM2/26/09
to clo...@googlegroups.com
I would much rather have a fast-math library that redefined common operators for numeric types only, and had the default +, <, > be multimethods. 

--
Cosmin Stejerean
http://offbytwo.com

Peter Wolf

unread,
Feb 26, 2009, 4:56:12 PM2/26/09
to clo...@googlegroups.com
OK had my coffee, and had several thoughts...

1 -- What are Strings? How should the Clojure programmer think about
them? Are they sequences, in which case all the sequence functions
should work. Or are they atomic built-in types like Integers and Floats?

2 -- There is already some type checking in + to deal with Integers,
Floats and infinite precision. Line 1212 of Numbers.java has the ops()
method which (I think) implements this

static Ops ops(Object x){
Class xc = x.getClass();

if(xc == Integer.class)
return INTEGER_OPS;
else if(xc == Double.class)
return DOUBLE_OPS;
else if(xc == Float.class)
return FLOAT_OPS;
else if(xc == BigInteger.class)
return BIGINTEGER_OPS;
else if(xc == Long.class)
return BIGINTEGER_OPS;
else if(xc == Ratio.class)
return RATIO_OPS;
else if(xc == BigDecimal.class)
return BIGDECIMAL_OPS;
else
return INTEGER_OPS;
}

3 -- Type hints can take the place of a fast-math library. The compiler
could automatically call the appropriate fast math routine when given
the necessary information.

So my vote is that String are atomic built in objects, and at least +, <
and > should work with Strings. The behavior should be just like Java,
so (+ "foo" 2) --> "foo2"

I don't think this will slow down math because the String case will be
the last "else if" in ops() and will only happen when the args are not
some sort of Number.

Finally, if it doesn't already, I would expect type hints to make things
faster. So, it should not be necessary to explicitly call a fast-math
library.

My 2 n00by cents...
P

Allen Rohner

unread,
Feb 26, 2009, 5:24:36 PM2/26/09
to Clojure

>
> So my vote is that String are atomic built in objects, and at least +, <
> and > should work with Strings.  The behavior should be just like Java,
> so (+ "foo" 2) --> "foo2"
>

-1

Concatenation is not addition. I'm almost opposed to numeric operators
all together. If we wrote (add 2 3), there would be no confusion at
all about what (add "foo" 2) should do, because you'd be writing (conj
"foo" (str 2))

If addition between a string and number is defined, what is
subtraction between a string and a number?

Allen



Laurent PETIT

unread,
Feb 26, 2009, 6:14:58 PM2/26/09
to clo...@googlegroups.com
Agree,

I even think there *could* be some utility in having the opposite behavior (but I'm not even sure about that) :

(+ "1" 2) --> 3 by + trying to cast its non numeric arguments before throwing an exception ...

--
Laurent

2009/2/26 Allen Rohner <aro...@gmail.com>

Phil Hagelberg

unread,
Feb 26, 2009, 6:23:31 PM2/26/09
to clo...@googlegroups.com
Laurent PETIT <lauren...@gmail.com> writes:

> > Concatenation is not addition. I'm almost opposed to numeric operators
> > all together. If we wrote (add 2 3), there would be no confusion at
> > all about what (add "foo" 2) should do, because you'd be writing (conj
> > "foo" (str 2))
>

> Agree

I agree regarding concatenation as well, but I think the case for
comparison of non-numerics is still pretty strong.

-Phil

Mike Benfield

unread,
Feb 26, 2009, 6:38:24 PM2/26/09
to Clojure


On Feb 26, 4:56 pm, Peter Wolf <opus...@gmail.com> wrote:

>
> So my vote is that String are atomic built in objects, and at least +, <
> and > should work with Strings.  The behavior should be just like Java,
> so (+ "foo" 2) --> "foo2"


I have an HP calculator. (I may get some of the details wrong here, I
haven't used it lately, but this is the gist of the thing.) + adds
numbers. + also concatenates lists. But wait, UNLESS the lists are
lists of numbers, in which case it adds numbers componentwise. Which
means you have to create a new function or operator if you actually
want to be sure you are concatenating lists. It's just dumb. The point
is, functions should not take on all kinds of extraneous
responsibilities that are completely unrelated to their purpose. +
adds numbers. str concatenates strings.

BTW, I don't know if this concept of "built in object" you keep saying
has any meaning in Clojure. Strings are just objects of the class
String, just like in Java.

Allen Rohner

unread,
Feb 26, 2009, 6:47:01 PM2/26/09
to Clojure

> I agree regarding concatenation as well, but I think the case for
> comparison of non-numerics is still pretty strong.
>
> -Phil

Are you referring to using <, >, =, with objects that implement
java.lang.Comparable?

i.e. given x.compareTo(y) == -1
(< x y)
=> true

I would find that useful.

Allen

Allen Rohner

unread,
Feb 26, 2009, 6:48:03 PM2/26/09
to Clojure
If we wrote (add 2 3), there would be no confusion at
> all about what (add "foo" 2) should do, because you'd be writing (conj
> "foo" (str 2))

I wrote this too hastily. This could more easily be written (str "foo"
2)

--Allen

Phil Hagelberg

unread,
Feb 26, 2009, 11:13:36 PM2/26/09
to clo...@googlegroups.com
Allen Rohner <aro...@gmail.com> writes:

>> I agree regarding concatenation as well, but I think the case for
>> comparison of non-numerics is still pretty strong.
>

> Are you referring to using <, >, =, with objects that implement
> java.lang.Comparable?
>
> i.e. given x.compareTo(y) == -1
> (< x y)
> => true
>
> I would find that useful.

I'm not sure of the details since I don't know much about Java, but that
sounds about right. I'm working on a date library, and having to use
functions like earlier? and later? rather than >, <, <=, and >= feels
awkward.

-Phil

Eric Tschetter

unread,
Feb 26, 2009, 11:38:19 PM2/26/09
to clo...@googlegroups.com
> I'm not sure of the details since I don't know much about Java, but that
> sounds about right. I'm working on a date library, and having to use
> functions like earlier? and later? rather than >, <, <=, and >= feels
> awkward.

You are probably thinking of dates as numerical longs rather than
actual "dates". If you take a step back from how the date is
represented, it is much clearer to say

(earlier? date1 date2)

than

(< date1 date2)

The latter is open to interpretation and would force any good
programmer to look up what less-than does on the dates in order to get
an intuition about what it *actually* means, whereas most everyone
would agree with what the former is intended to mean. '<' could be
defined as "happened after" and be a *valid* function definition. If
"earlier?" was defined as "happened after" it would be considered a
bug.

I realize this is just one use case, but overloading operators can
often decrease readability. That said, it can also be extremely
convenient and make for more flexible libraries, so perhaps the
argument about a fast math library and convenient non-math behavior by
default is the way to go...

Btw, if you are implementing a date library, I would strongly
recommend looking at Joda Time. You could probably just write some
clojure wrappers for the library and be done with it (of course, that
would require that the Joda Time jar be on the classpath...).

--Eric Tschetter

Konrad Hinsen

unread,
Feb 27, 2009, 2:10:57 AM2/27/09
to clo...@googlegroups.com
On 26.02.2009, at 20:18, Phil Hagelberg wrote:

> One approach that's been proposed in #clojure is to make these
> functions
> more capable by default, but then provide a fast-math library that
> could
> redefine them in terms of numerics-only. I'm a big fan of functions
> doing the most helpful thing by default but being able to offer better
> speed when you need it.
>
> Convenience vs speed is always a trade-off, but I think convenience
> should win in the default case. What do others think about this?

My first reaction was "I agree". Speaking in purely abstract terms, I
also prefer convenience to speed as a default. But then I started
thinking a bit about the concrete case you are proposing.

First of all, it's a minor point in Lisp languages. There is nothing
special to functions like +. If you want another + than the one in
clojure.core, all it takes is some manipulation of the use, require,
and refer declarations at the top of your library or script. We are
not in the same situation as languages where there is a small number
of special operators. Nor is + so frequently used in other core
functions that having a second implementation would be pointless.

Second, it is not at all trivial to come up with good scheme for
polymorphic arithmetic operators. Given

(+ a b c d)

with a b c d of four different types, how do you dispatch to a
specific implementation? Dispatch on the type of the first argument?
The first two, with longer argument lists handled by reduction?
Introduce a priority hierarchy in the implementations? Or a
capability list? Among other criteria, it depends on what features of
addition you want to guarantee. Many people would expect
commutativity, based on their daily life experience with numbers, but
that's not easy to guarantee, and it would rule out using addition
for string concatenation, for example. (This is not to say that I am
in favour of string "addition", quite on the contrary, it's just a
nice illustration.)

I propose that instead of discussing that the default implementation
should be, we first start writing a "generic arithmetic" library
module in clojure.contrib and see how it works out in practice. I
expect the main effort to be test-driving the module rather than
implementing it, as the design choices are critical and probably much
harder than the implementation. Any volunteers?

Konrad.

Christian Vest Hansen

unread,
Feb 27, 2009, 4:51:57 AM2/27/09
to clo...@googlegroups.com
I think having <, >, <=, >= be based on Comparable has been discussed before.

And the conclusion was that it was a bad idea, because in Java:

user=> (.compareTo (Integer. "10") (Long. "10"))
java.lang.ClassCastException: java.lang.Long cannot be cast to
java.lang.Integer (NO_SOURCE_FILE:0)

And:

user=> (.equals (Integer. "10") (Long. "10"))
false

Whereas in Clojure:

user=> (< (Integer. "10") (Long. "10"))
false
user=> (= (Integer. "10") (Long. "10"))
true

Given these consequences, I think the current behavior is the best compromise.

>
> Allen
>
> >
>



--
Venlig hilsen / Kind regards,
Christian Vest Hansen.

Phil Hagelberg

unread,
Feb 27, 2009, 12:03:18 PM2/27/09
to clo...@googlegroups.com
Christian Vest Hansen <karma...@gmail.com> writes:

>> Are you referring to using <, >, =, with objects that implement
>> java.lang.Comparable?
>>
>> i.e. given x.compareTo(y) == -1
>> (< x y)
>> => true
>>
>> I would find that useful.
>
> I think having <, >, <=, >= be based on Comparable has been discussed before.
>
> And the conclusion was that it was a bad idea, because in Java:
>
> user=> (.compareTo (Integer. "10") (Long. "10"))
> java.lang.ClassCastException: java.lang.Long cannot be cast to
> java.lang.Integer (NO_SOURCE_FILE:0)
>
> And:
>
> user=> (.equals (Integer. "10") (Long. "10"))
> false

Curses, Java! Foiled again.

> Given these consequences, I think the current behavior is the best compromise.

Agreed. Am curious as to what the idiomatic way to check to see if one
string is alphabetically greater than another is though.

-Phil

David Nolen

unread,
Feb 27, 2009, 12:29:01 PM2/27/09
to clo...@googlegroups.com
What about something like:

(defn gt [str1 str2]
  (first (sort [str1 str2])))

(gt "Zoe" "Bob") ; -> "Bob"

Achim Passen

unread,
Feb 27, 2009, 12:34:22 PM2/27/09
to clo...@googlegroups.com
Hi!

Am 27.02.2009 um 18:03 schrieb Phil Hagelberg:

Agreed. Am curious as to what the idiomatic way to check to see if one
string is alphabetically greater than another is though.

"compare" seems to do the right thing in most cases. You could define shorthands if that's too much typing:

user=> (defn << [a b] (neg? (compare a b)))
#'user/<<
user=> (defn >> [a b] (pos? (compare a b)))
#'user/>>
user=> (>> "d" "abc")
true
user=> (<< "d" "abc")
false
user=> (<< (Integer. "3") (Long. "4"))
true
user=> (>> (Integer. "3") (Long. "4"))
false

Kind regards,
achim

Phil Hagelberg

unread,
Feb 27, 2009, 12:35:24 PM2/27/09
to clo...@googlegroups.com
David Nolen <dnolen...@gmail.com> writes:

> What about something like:
>
> (defn gt [str1 str2]
>   (first (sort [str1 str2])))
>
> (gt "Zoe" "Bob") ; -> "Bob"

If nothing exists yet, defining <, >, <=, and >= in str-utils might work.

-Phil

Reply all
Reply to author
Forward
0 new messages