Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

methods and types

6 views
Skip to first unread message

Jish Karoshi

unread,
Apr 15, 2001, 5:28:06 AM4/15/01
to
Hello.

I have been using mostly java for the last 5 or 6 years and did
C and C++ previously. I have never used smalltalk. That might
explain why I need to ask the following basic question:

How do you design programs when you can't specify types? If all
I can specify in a method definition are the names of the arguments,
then how does anybody know how to use that method without knowing
about the implementation of the method? The same thing goes for
the return value. How do I know what I am supposed to send and
what I can expect to get back unless those types have been announced
in the method signature or I understand how the method I want to
call is implemented?

I have only been looking into ruby for a brief time, but from what
I can tell, it seems like everybody on a project would have to know
implementation details of the whole system in order to participate.
I don't understand how to chop a system up into little black boxes
if I don't have type information.
I especially don't understand how anyone would ever be able to
implement a distributed system in this way.

I feel that I must be missing something obvious and important here!
If somebody could clue me in I would really appreciate it.
Thanks,

- jish

_________________________________________________________________
Get your FREE download of MSN Explorer at http://explorer.msn.com

Bernd Lorbach

unread,
Apr 15, 2001, 7:31:33 AM4/15/01
to
Jish Karoshi wrote:

> How do you design programs when you can't specify types? If all
> I can specify in a method definition are the names of the arguments,
> then how does anybody know how to use that method without knowing
> about the implementation of the method?

If all you know about a function is the name of the arguments,
you cannot do serious programming. But if you know the types
of the arguments, you cannot do neiter! Imagine a function

pid_t waitpid(pid_t wpid, int *status, int options)

do you exacly know what is does, or what meaning return values
have, without having seen any documentation? Type information
does not help much here.

The clue is, if you write a function, and want it get used by
others, you have to provide some documentation, either as
comments, or elsewhere. In the documentation, you also
can provide type informatin - if the functin is restricted
to some types. If you just know parameter types, you start
trying out - and programming by try and error might be fun,
but is not a serious thing.

In Ruby, you need not specify types, as often functions can take
more than one type.

def add_up(a,b)
a + b
end

It adds what you give - It cill act on every type having
a method "+" defined. if you pass strings it will result
strings. If you pass integer, it will return integer:

add_up("Hello", "World") --> "HelloWorld"
add_up(22, 33) --> 55

That is what I call polymorphic!
Look at C++, where for that class of polymorphism you need
templates. And I cannot imagine something like

template <class T_IN, class T_OUT>
T_OUT process(T_IN input, T_OUT output)

is more intuitive (or even easier to program) like the Ruby

def process(input, output)

> The same thing goes for
> the return value.

Shure!

> I have only been looking into ruby for a brief time, but from what
> I can tell, it seems like everybody on a project would have to know
> implementation details of the whole system in order to participate.
> I don't understand how to chop a system up into little black boxes
> if I don't have type information.

I do not agree here. The C++ STL has more in common with a black box
(at least for beginners) than any Ruby function.

Also, I do not consider black boxes to be that bad.
It does a great deal in hiding implementation details.

> I especially don't understand how anyone would ever be able to
> implement a distributed system in this way.

Do not mind. You will learn as you get used to OOP, type abstraction
and software engineering :-)

> I feel that I must be missing something obvious and important here!
> If somebody could clue me in I would really appreciate it.

I have to admit that there is not so much documenation on Ruby like
on other programming languages, but if you want to learn something
about the concepts of Ruby, try

http://www.ruby-lang.org/en/doc.html

The "Ruby User's Guide" is a good point to start with.

Greetings, /Bernd

Bill Kelly

unread,
Apr 15, 2001, 7:35:35 AM4/15/01
to

From: "Jish Karoshi" <karos...@hotmail.com>

>
> I have been using mostly java for the last 5 or 6 years and did
> C and C++ previously. I have never used smalltalk. That might
> explain why I need to ask the following basic question:
>
> How do you design programs when you can't specify types? If all
> I can specify in a method definition are the names of the arguments,
> then how does anybody know how to use that method without knowing
> about the implementation of the method? The same thing goes for
> the return value. How do I know what I am supposed to send and
> what I can expect to get back unless those types have been announced
> in the method signature or I understand how the method I want to
> call is implemented?

As one who felt strongly about how beneficial static typing (compile-
time type checking) must be to keeping a whole class of "problems"
out of programs, as recently as a couple years ago . . . . I anticipate
you'll find that, somewhat like 'Hungarian notation', static typing
doesn't really communicate quite as much to the programmer as it is
often credited to.

Even in a statically-typed world, how often does one find oneself not
bothering to look at what kind of object a routine says it wants as a
parameter; but instead just passing in any ol' object type, relying on
a compiler error to inform us about what kind of object the routine
really wants? Probably we look at the function declaration to see
what kind of object that method wants to work with. :)

Consider the following three declarations:

void moveWidget (Widget& aWidget) {
// don't look at the implementation yet
}

template<class T> void moveWidget (T& aWidget) {
// don't look at the implementation yet
}

def moveWidget(aWidget)
# don't look at the implementation yet
end

What kind of object would you supposed we would pass to the last two?

"Well, something that acts like a Widget." would probably be my own
answer.

So it seems we may be now to your question, where it seems you may be
asking: yes, but how do I know if I have something that acts like a
Widget or not, without having to inspect the code of everything that
*says* it wants a Widget, to see what capabilities all this various
code demands of a Widget? :)

Well, hopefully someone will write a better explanation than mine in
this thread, because my answer is: I don't know. Only, it doesn't
seem that this is a huge problem (in practice.) I think one may get
used to thinking in terms of the "protocol" required to support a
certain category of operations on an object. . . . Another thought:
What came first, the Widget or the code that uses it? Somewhere in
the code, I'd expect to either encounter: the quintessential Widget
class; or, something defining what it means to be "like a Widget" in
the abstract (say an Interface in Java; perhaps a mix-in module in
Ruby?)

Anyway, it's late, I may be rambling. :)

Here's a link that may be of interest:
http://www.pragmaticprogrammer.com/cgi-local/pragprog?JavaIsUntyped


Regards,

Bill


Dennis Decker Jensen

unread,
Apr 15, 2001, 6:12:29 PM4/15/01
to
Hello,

Jish Karoshi wrote:
# How do you design programs when you can't specify types? If all
# I can specify in a method definition are the names of the arguments,
# then how does anybody know how to use that method without knowing
# about the implementation of the method? The same thing goes for
# the return value. How do I know what I am supposed to send and
# what I can expect to get back unless those types have been announced
# in the method signature or I understand how the method I want to
# call is implemented?

You rely on named methods and named parameters instead of named types :-)

Instead of relying on some named type, you rely on an object being able to
respon to some methods or operators, which essentially IS the type of the
object.

Wheither you use explicitly declared types or not in a programming language,
you still make assumptions about the Usage Type without knowing the
Implementation Type.

It takes some time to get used to though, but you'll soon realize that
static typing in many cases isn't necessary.

You often gain type-safety by imposing the "meta-level" structures (in your
head...) on the code expressions in your code. Types can help you with that,
but they aren't necessary. They become redundant, if you can impose the
"meta-level" structures some other way, e.g. by clear naming of classes,
objects, methods and parameters.

Here you can read more about all the benefits of dynamic typing:
http://www.c2.com/cgi/wiki?BenefitsOfDynamicTyping

I hope that clued you in :-)

Regards,

Dennis Decker Jensen

Jish Karoshi

unread,
Apr 16, 2001, 1:07:26 PM4/16/01
to
Bernd Lorbach wrote:

>
>Jish Karoshi wrote:
>
> > How do you design programs when you can't specify types? If all
> > I can specify in a method definition are the names of the arguments,
> > then how does anybody know how to use that method without knowing
> > about the implementation of the method?
>
>If all you know about a function is the name of the arguments,
>you cannot do serious programming. But if you know the types
>of the arguments, you cannot do neiter! Imagine a function
>
> pid_t waitpid(pid_t wpid, int *status, int options)
>
>do you exacly know what is does, or what meaning return values
>have, without having seen any documentation? Type information
>does not help much here.
>
>The clue is, if you write a function, and want it get used by
>others, you have to provide some documentation, either as
>comments, or elsewhere. In the documentation, you also
>can provide type informatin - if the functin is restricted
>to some types.

How could any function that does any sort of useful work not be
restricted to some types? All I can come up with is some sort
of collection classes, although I am not even convinced of that
one. A function must be able to rely on its arguments having some
sort of attributes or operations that are that function's reason
for existing I would think.

>If you just know parameter types, you start
>trying out - and programming by try and error might be fun,
>but is not a serious thing.
>
>In Ruby, you need not specify types, as often functions can take
>more than one type.
>
> def add_up(a,b)
> a + b
> end
>
>It adds what you give - It cill act on every type having
>a method "+" defined. if you pass strings it will result
>strings. If you pass integer, it will return integer:
>
> add_up("Hello", "World") --> "HelloWorld"
> add_up(22, 33) --> 55
>
>That is what I call polymorphic!

So is it true that in this system the first thing that every
function ought to do, in order to be robust, is check to see
that certain operations have been implemented by the arguments
that are passed to it?
Is the definition of a type in ruby or a language like it the
set of all combinations of operations which an object has
implemented?
Also, since the return types of functions are not defined, how
does function A know that checking for the existence of
function B in some parameter is enough to do something
useful with the parameter? Do I really need to check the type
of every argument passed to my method and then check the
return type of every method I call from those arguments?

Please understand that I am not trying to get into a static
types good or bad argument here, I am just trying to understand
what sort of artifacts would have to be generated in the
design stage of a project if that project was going to be
implemented in ruby, and what sort of process one would use
to build a robust implementation using ruby.

...

> > I have only been looking into ruby for a brief time, but from what
> > I can tell, it seems like everybody on a project would have to know
> > implementation details of the whole system in order to participate.
> > I don't understand how to chop a system up into little black boxes
> > if I don't have type information.
>
>I do not agree here. The C++ STL has more in common with a black box
>(at least for beginners) than any Ruby function.
>
>Also, I do not consider black boxes to be that bad.
>It does a great deal in hiding implementation details.

I think you might have misunderstood what I wrote (or meant
to write anyway) which is black boxes are good. My question
is how do you make a black box in ruby, since I don't know
a way to do non-trivial projects without them? Thanks,

Dave Thomas

unread,
Apr 16, 2001, 1:45:08 PM4/16/01
to
"Jish Karoshi" <karos...@hotmail.com> writes:

> So is it true that in this system the first thing that every
> function ought to do, in order to be robust, is check to see that
> certain operations have been implemented by the arguments that are
> passed to it? Is the definition of a type in ruby or a language
> like it the set of all combinations of operations which an object
> has implemented? Also, since the return types of functions are not
> defined, how does function A know that checking for the existence of
> function B in some parameter is enough to do something useful with
> the parameter? Do I really need to check the type of every argument
> passed to my method and then check the return type of every method I
> call from those arguments?

Possibly the issue is broader than this.

First look at popular languages such as Java, where type-safety is
largely defined in terms on interfaces. If an object implements an
interface, then it is an acceptable parameter for a method that
required that interface, or for assignment to a variable of that type.

What has this gained you?

Well, you know that you can call the methods in that interface on the
passed object, and you will not get a runtime failure because they
don't exist.

Anything else? Not really, because the specification of type by
interface alone totally ignores semantics: I could define

public class Dave implements Stack {
public void push(Pushable o) {
System.out.println("Refusing to push: I'm tired");
}
public Pushable pop() {
return new PushableInt(99);
}
}

Type safe, but semantic gibberish.

Now, turn the question around. What do you gain by abandoning this
limited form of safety?

Well, we gain immense flexibility. Refactoring Smalltalk and Ruby is
trivial compared to (say) Java. Things just move around. There's no
need to jump through hoops to satisfy the compiler.

We gain substantial testability. You can construct mock objects very,
very easily, and test out code with far less overhead. You can test
objects before the classes they rely on are finished (or even
started). This ability to test partial classes also lends itself to
easier incremental testing.

We gain expressiveness. I don't know how to describe this one
objectively: I just know that I find this kind of code speaks to me
more directly: I'm dealing with objects, not object categories.

This is not a thing that can be argued rationally. I was a strong-
typing advocate for years, and was nervous when I used languages such
as Smalltalk and Ruby. However, I now find Java a very frustrating
language to use, and find myself writing higher-quality code in
Ruby. In the end, the only way to find out is to try it for yourself
and see. Write some Ruby code, and wait until you experience that
a-ha! moment. Then write some more code until you start developing an
idiomatic style. Get comfortable with RubyUnit or Lapidary. Then take
on a largish project, and see what you thing.

Regards

Dave

Stephen White

unread,
Apr 16, 2001, 4:02:02 PM4/16/01
to
On Tue, 17 Apr 2001, Dave Thomas wrote:

> Now, turn the question around. What do you gain by abandoning this
> limited form of safety?
>
> Well, we gain immense flexibility. Refactoring Smalltalk and Ruby is
> trivial compared to (say) Java. Things just move around. There's no
> need to jump through hoops to satisfy the compiler.

The way I think of this is "easy in, easy out". The data goes into the
method easily since you can just define it, and the data comes out of
the method easily since you can just use it. There's no pre-coding to
condition the program to handle the data.

Types are meta-data, so embedding types into code is just embedding higher
level data into code. When changing code in strongly typed languages, the
process is essentially a parallel re-write. Change the code, change the
types, change the code, change the types.

Removing type information loosens the strong binding between code and
data, like separating embedded code from HTML. Refactoring becomes a
linear process of change the code, change the code.

Ruby isn't a type-less language. The type information has been moved
into the data and moves around with the data. The meta-data has been
normalised to the data and code can get back to the job of being
program instructions.

Another aspect I've noticed is that in C, I was forever passing around
complicated structures. In Ruby, I'm mainly using numbers, strings and
arrays. Communication between methods and objects is simpler, and I
don't really know why.

I suspect the lack of typing makes programmers work just that bit
harder to make their classes more accessible. A little bit more
up-front work, a lot more flexibility.

--
spw...@chariot.net.au

Yukihiro Matsumoto

unread,
Apr 17, 2001, 12:51:46 AM4/17/01
to
Hi,

In message "[ruby-talk:13709] Re: methods and types"


on 01/04/17, Dave Thomas <Da...@PragmaticProgrammer.com> writes:

|This is not a thing that can be argued rationally. I was a strong-
|typing advocate for years, and was nervous when I used languages such
|as Smalltalk and Ruby.

More than 10 years ago, I was an evangelist of OOP with static typing.
Back then, the dominant OOPL was Smalltalk. C++ was immature that
lacked features it currently have (multiple inheritance and dynamic
cast for example).

Things have changed since then. Most people become familiar with
static typed OOP via C++ and Java. I become an evangelist of dynamic
OOP thru Ruby.

matz.

Matt Armstrong

unread,
Apr 17, 2001, 1:38:21 AM4/17/01
to
On Tue, Apr 17, 2001 at 02:45:08AM +0900, Dave Thomas wrote:
> What do you gain by abandoning this limited form of safety? (type
> checking)
>
> <snip>

>
> We gain substantial testability. You can construct mock objects very,
> very easily, and test out code with far less overhead. You can test
> objects before the classes they rely on are finished (or even
> started). This ability to test partial classes also lends itself to
> easier incremental testing.

I'm curious about this. Coming from a mainly C/C++/Perl background, it
isn't obvious to me how this benefit of "easy testability" is actually
realized. I've been basically coding in Ruby just like I'd code in Perl
(except using classes all over the place). But my write/test habits
haven't changed.

What kinds of things can I do to make my Ruby code easier to test?

--
matt

Jish Karoshi

unread,
Apr 17, 2001, 3:59:06 AM4/17/01
to

Dave Thomas wrote:
>This is not a thing that can be argued rationally. I was a strong-
>typing advocate for years, and was nervous when I used languages such
>as Smalltalk and Ruby. However, I now find Java a very frustrating
>language to use, and find myself writing higher-quality code in
>Ruby. In the end, the only way to find out is to try it for yourself
>and see. Write some Ruby code, and wait until you experience that
>a-ha! moment. Then write some more code until you start developing an
>idiomatic style. Get comfortable with RubyUnit or Lapidary. Then take
>on a largish project, and see what you thing.

I am busily learning ruby, and I see some things that I like or
I wouldn't bother asking questions. My problem is that I can't
really do what you suggest about taking on a largish project and
see what I think. I need to be reasonably sure that something
is going to be workable before I commit resources to it. I have
seen enough ruby already to agree with people here that ruby is
probably more enjoyable to code in than java or c. But I can't
make design decisions for projects based on whats more enjoyable.
Well, somewhat I can, but you know what I mean.

I want my team to start using ruby instead of perl for all the
various odds and ends tasks that perl gets used for now. I have
seen enough perl code to last me a lifetime. Good code in perl
is fine, but there's something about bad code in perl thats worse
than bad code in other languages, something very HP-Lovecraft-
mad-servants-of-the-elder-gods-chattering-in-the-extradimensional-
insect-language kind of bad that makes my head hurt when I have
to read it. Which is why I started looking at ruby in the first
place.
I am just very curious to know what tools and what methods people
use to successfully implement medium sized projects in ruby. More
specifically, if we changed from java to ruby for a 20-30 man year
project, what would we need to change in our design process and
our other tools in order to end up on schedule with a robust system?
Do tools even exist that can cope with ruby? We use rational for
UML etc, and mercury for testing currently.
This is why I asked about the argument types question in the first
place, because it seems like it would force a change in the way
you do things when you design and iterate, and I want to know what
those changes are so I can make an informed choice.

I know thats a really broad question, and I appreciate the time you
and others have already taken to try to enlighten me. I guess what
I am looking for in a nutshell is a pointer to the method that
works best with ruby. Thanks,

Dave Thomas

unread,
Apr 17, 2001, 8:39:33 AM4/17/01
to
ma...@lickey.com (Matt Armstrong) writes:

> I'm curious about this. Coming from a mainly C/C++/Perl background,
> it isn't obvious to me how this benefit of "easy testability" is
> actually realized. I've been basically coding in Ruby just like I'd
> code in Perl (except using classes all over the place). But my
> write/test habits haven't changed.
>
> What kinds of things can I do to make my Ruby code easier to test?

I find I do a couple of things differently. First, because method and
class definitions are so painless, I find myself writing more of them,
and each method tends to be smaller. As I tend to test each method as
I write it, that leads to more frequent tests. As the methods are so
small, the tests tend to pass first time too, so there's a rhythm that
builds up.

Second, I use Ruby's type tolerance to advantage during testing.
Perhaps I have a routine that's going to add some data to a file. I
know it uses '<<' to do the append, so during testing I can pass in an
empty string, and then check the value in that string when the routine
exits. Obviously you _can_ do this in other languages, but being able
to do it without any messing about whatsoever makes the experience far
more enjoyable in Ruby (which in turn means that you end up doing it
more often :).


Regards


Dave

Anders Bengtsson

unread,
Apr 17, 2001, 9:29:28 AM4/17/01
to
--- Dave Thomas <Da...@PragmaticProgrammer.com> wrote:
> Second, I use Ruby's type tolerance to advantage
> during testing.
> Perhaps I have a routine that's going to add some
> data to a file. I
> know it uses '<<' to do the append, so during
> testing I can pass in an
> empty string, and then check the value in that
> string when the routine
> exits.

This may be an instance of some design pattern about
strings. When programming in Ruby i often start out
with string representations of data, and only later
change it to use a more specific class.

An example could be an adress book application. In
Java I would probably have a Name class from the
start, that knows about family names and given names,
and having a Person class with a field of type Name.
In Ruby I would use a String for the name, up to the
moment that I needed to operate on parts of the name,
when I would simply switch to using Name instances,
without having to modify Person.

/Anders


=====
__________________________________________________
Anders Bengtsson ndrsb...@yahoo.se
Stockholm, Sweden

_____________________________________________________
Do You Yahoo!?
Ditt...@yahoo.se - skaffa en gratis mailadress phttp://mail.yahoo.se

Dave Thomas

unread,
Apr 17, 2001, 9:59:28 AM4/17/01
to
Anders Bengtsson <ndrsb...@yahoo.se> writes:

> This may be an instance of some design pattern about
> strings. When programming in Ruby i often start out
> with string representations of data, and only later
> change it to use a more specific class.

I think that's a good refinement of the process. Ruby allows you to
defer making those kinds of decisions. Not having to decide up front
makes designs more flexible, and encourages experimentation.


Dave

Dave Thomas

unread,
Apr 17, 2001, 10:06:14 AM4/17/01
to
"Jish Karoshi" <karos...@hotmail.com> writes:

> I want my team to start using ruby instead of perl for all the
> various odds and ends tasks that perl gets used for now. I have
> seen enough perl code to last me a lifetime. Good code in perl
> is fine, but there's something about bad code in perl thats worse
> than bad code in other languages, something very HP-Lovecraft-
> mad-servants-of-the-elder-gods-chattering-in-the-extradimensional-
> insect-language kind of bad that makes my head hurt when I have
> to read it. Which is why I started looking at ruby in the first
> place.

And that sounds like an eminently sensible plan :)

> I am just very curious to know what tools and what methods people
> use to successfully implement medium sized projects in ruby. More
> specifically, if we changed from java to ruby for a 20-30 man year
> project, what would we need to change in our design process and
> our other tools in order to end up on schedule with a robust system?

I don't think it would be prudent to use Ruby for a 20--30 man-year
project until I'd had a chance to let the team try it on a smaller
scale. I'm as big a Ruby advocate as they get, but I'd still say that
Ruby is far behind (say) Java in terms of ancillary support: there
are thousands of books on Java, and (so far :) one on Ruby. There are
Java training courses, and Java consultants, and ...

However, I also think that with a team that knows Ruby, and that is
comfortable with it, you'd see significant benefits using Ruby over
Java in many problem domains.

> Do tools even exist that can cope with ruby? We use rational for
> UML etc, and mercury for testing currently. This is why I asked
> about the argument types question in the first place, because it
> seems like it would force a change in the way you do things when you
> design and iterate, and I want to know what those changes are so I
> can make an informed choice.

Perhaps a place to start would be with the XP folks, and the C3
project. They delivered a project of roughly that size in Smalltalk,
and Smalltalk's a near relative of Ruby. Maybe you could ask on the XP
mailing list about the differences that static typing would make to a
project.

Regards


Dave

Jim Menard

unread,
Apr 17, 2001, 10:28:15 AM4/17/01
to
"Jish Karoshi" <karos...@hotmail.com> writes:

> Good code in perl
> is fine, but there's something about bad code in perl thats worse
> than bad code in other languages, something very HP-Lovecraft-
> mad-servants-of-the-elder-gods-chattering-in-the-extradimensional-
> insect-language kind of bad that makes my head hurt when I have
> to read it.

Thank you. That went directly into my .sig quotes file.

Jim
--
Jim Menard, ji...@io.com, http://www.io.com/~jimm/


"Good code in perl is fine, but there's something about bad code in perl
thats worse than bad code in other languages, something very HP-Lovecraft-
mad-servants-of-the-elder-gods-chattering-in-the-extradimensional-
insect-language kind of bad that makes my head hurt when I have to read

it." -- Jish Karoshi in comp.lang.ruby

B_M

unread,
Apr 23, 2001, 1:43:27 PM4/23/01
to
Hello

When I assign a string to $0, it's truncated to a size relative to the original $0's size.

Example:

mike@photon:~$ cat a.rb
$0 = "-" * 50
p $0.size
mike@photon:~$ ruby a.rb
9
mike@photon:~$ ln a.rb abcdef.rb
mike@photon:~$ ruby abcdef.rb
14
mike@photon:~$ ln a.rb aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.rb
mike@photon:~$ ruby aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.rb
47

I need $0 assigmnent to run a script using 'load'. Should I rename my script to something like loader______________________.rb to allow long subscript names? :)

On Win32, it seems to always be truncated to 25 chars.


Mike

Yukihiro Matsumoto

unread,
Apr 24, 2001, 2:38:07 AM4/24/01
to
Hi,

In message "[ruby-talk:14094] Assignment to $0 is truncated"


on 01/04/24, B_M <bmb...@magicnet.org> writes:

|When I assign a string to $0, it's truncated to a size relative to the original $0's size.

This is a bug.

--- ruby.c 2001/03/13 05:48:33 1.36.2.4
+++ ruby.c 2001/04/24 06:37:36
@@ -879,5 +879,5 @@
#ifndef __hpux
- if (i > len) {
- memcpy(origargv[0], s, len);
- origargv[0][len] = '\0';
+ if (i < len) {
+ memcpy(origargv[0], s, i);
+ origargv[0][i] = '\0';
}

0 new messages