Ruby type-safe? Ruby strongly/weakly typed? Ruby pitfalls?

A

Arlen Cuss

[Note: parts of this message were removed to make it a legal post.]

...

irb(main):001:0> 4.class
=> Fixnum
irb(main):002:0> (4.to_i*2).class
=> Fixnum

The type doesn't change in your example.
Moreover, the type will never change in its lifetime, as Eivind mentioned.
Sure, they have plenty of methods that return different types (String#split,
Integer#to_f, Symbol#to_proc [1.9/Rails]...), but nothing alters the
object's type itself.

Arlen
 
R

Rule.rule

irb(main):001:0> 4.class
=> Fixnum
irb(main):002:0> (4.to_i*2).class
=> Fixnum

The type doesn't change in your example.


Well, I certainly beg to disagree.

By any meaningful type-semantics, the type must be considered
to change when the set of methods it responds to changes.
That Ruby still calls it by the same name, apparently, only aggravates
the confusion here.

I guess, in Ruby, a type is best defined by its behaviour?
Before the redefinition of Fixnum above, for example, calling 3.twice
would throw an exception,
while after, it will return a value, which is obviously a different
behaviour.
 
E

Eric Mahurin

[Note: parts of this message were removed to make it a legal post.]

Please stop responding to this anonymous rule.rule.rule TROLL.

The obviously biased comments he put on wikipedia about ruby should be
removed. The ranting seems to be all about typing. Compare to the Python
page that has about the same typing (duck) as Ruby (with different syntax).
 
T

ThoML

Please stop responding to this anonymous rule.rule.rule TROLL.

It seems s/he created this gmail account just for asking this question
and IMHO s/he mixes up dichotomies like dynamic/static, strong/weak
typing. According to recent edit of the wiki page, s/he is not alone
though.
 
R

Rule.rule

[Note: parts of this message were removed to make it a legal post.]

Please stop responding to this anonymous rule.rule.rule TROLL.

The obviously biased comments he put on wikipedia about ruby should be
removed. The ranting seems to be all about typing. Compare to the Python
page that has about the same typing (duck) as Ruby (with different syntax).

I am sorry if I got totally misunderstood by some here,
just need to point out my intention was certainly none of "trolling".

I only recently started to really "catch up" on Ruby (and Rails) and
trying to get my grips around this innovative and peculiar language.
But I do have a long background in OOP, as well as functional
languages and -- *shudder* -- type-theory,
and also long experience with several industrial projects both
building and maintaining large-scale applications.

While I certainly can see the strong sides and potentials in Ruby, I
also found some characteristics
that, from my own background and experience, would seem obviously
problematic in real-life development.

If my posts look "biased", it is not because I dont see the good
things in Ruby
-- they are, well, good things and they just dont concern me.

While the ones I brought up on wiki, certainly do concern me.
And, right or wrong, I felt a worthwhile way of possibly getting some
more understanding
or clarification of those issues, would be to summarize and put them
up on the web,
to get some feedback from other users that may have different
experience -- thats all.

Finally: If you -- for whatever reason -- dont like this thread, why
dont you just ignore it and let those who want or not want to
participate or read it, simply decide by themselves?
 
B

Bill Kelly

From: "Rule.rule said:
I only recently started to really "catch up" on Ruby (and Rails) and
trying to get my grips around this innovative and peculiar language.
But I do have a long background in OOP, as well as functional
languages and -- *shudder* -- type-theory,
and also long experience with several industrial projects both
building and maintaining large-scale applications.

While I certainly can see the strong sides and potentials in Ruby, I
also found some characteristics
that, from my own background and experience, would seem obviously
problematic in real-life development.

If you have a really long background in OOP, you probably started
with Smalltalk right? (Since its inventor, Alan Kay, coined the
term Object Oriented.)

<grin>

Anyway - Ruby has strong/dynamic typing like Smalltalk. (Ignoring
Smalltalk's #become ... :)

There are plenty of examples of "real-life" development in both
Ruby and Smalltalk...

If your prior OOP experience has been solely with watered-down
OOP languages like C++ or Java, then, my condolences. But static
typing is not required for "real-life" development. :)


Regards,

Bill
 
S

s.ross

If your prior OOP experience has been solely with watered-down
OOP languages like C++ or Java, then, my condolences. But static
typing is not required for "real-life" development. :)

I second the condolences. I don't know what background, specifically,
rule.rule comes from but I come from a C/C++ one (and yes, even
Smalltalk). What people who've seen (ice cream|hotdogs) made is that
if you knew what went into them you'd never eat them. Well, I feel the
same about C++. C was a perfectly fine language, and ISO C was even
ok, although not as nice because it was beginning to acquire syntactic
angst.

The hoop-jumping required to create a C++ front-end and runtime is
absolutely nuts. Remember, no matter how well intentioned the
implementation, object orientation is grafted onto C to form C++. It
was not a guiding principle in the underlying language design. Java,
bless its heart, is so C++ influenced that it's hard to love and C# is
almost exactly the same.

Breakaway languages that are both object oriented and dynamic are
coming into the mainstream in Python and Ruby. In the "hmmmm, let's
wait and see" category for me are Haskell and Erlang, although large
projects already entrust their success to these languages. Lua may be
considered out of the mainstream, yet I believe the UI for Adobe
Lightroom was written using it.

The moral of the story is that the number of languages in use today is
expanding and dynamic typing does not appear to be holding anybody
back. It didn't hold back a generation of Perl programmers or PHP
programmers and it's unlikely to hold back Ruby programmers. Looked at
another way: If Ruby can make you more productive, make your code more
readable and concise at the same time(!), and can be tested
thoroughly, then programmers have more time to fix edge case bugs that
one might attribute to incorrect type inferencing.

Another $.02 from my piggy-bank.
 
R

Rule.rule

If you have a really long background in OOP, you probably started
with Smalltalk right? (Since its inventor, Alan Kay, coined the
term Object Oriented.)

Hi Bill,

No, smalltalk is one language I never used, I'm afraid.
My first experience with OOP was a dialect called Pascal-Plus in the
early 80'ies, then C++, Delphi, Java...
<grin>

Anyway -Rubyhas strong/dynamic typing

Dynamic, sure.
But re strongly, yes, I have seen this claim in several places
(including from Matz).
As I pointed out on wiki, though, it seems to depend on what
definition one refers to.
It seems to me, Matz is using Defs #3 and/or #6 in the list I gave
there:
http://en.wikipedia.org/wiki/Ruby_(programming_language)

But Ruby immediately clashes with my own "intuitive concept" of
"strongly typed",
when you can do things like:
x = 2; x = "a"
without getting an error,
and you also have untyped lists/arrays, etc, in the language.

Looks too much like Perl or something, to me :)
like Smalltalk. (Ignoring
Smalltalk's #become ... :)
There are plenty of examples of "real-life" development in bothRubyand Smalltalk...
If your prior OOP experience has been solely with watered-down
OOP languages like C++ or Java, then, my condolences.

OK, I humbly have to accept your condolences then :)
But static typing is not required for "real-life" development. :)

Obviously not, I have also seen a few sizable systems entirely built
in PHP or Perl or whatever,
after they started out as a "pilot" and later they didnt have the
funds to rebuild them,
decided to go live with what they had, and kept expanding on it...

But how easy and fun are they to maintain and extend, when over the
years the
original creators leave the project and new people join, etc?

With Ruby, I need more experience to judge, I guess :)
 
B

Bill Kelly

From: "Rule.rule said:
Dynamic, sure.
But re strongly, yes, I have seen this claim in several places
(including from Matz).
As I pointed out on wiki, though, it seems to depend on what
definition one refers to.
It seems to me, Matz is using Defs #3 and/or #6 in the list I gave
there:
http://en.wikipedia.org/wiki/Ruby_(programming_language)

But Ruby immediately clashes with my own "intuitive concept" of
"strongly typed",
when you can do things like:
x = 2; x = "a"
without getting an error,
and you also have untyped lists/arrays, etc, in the language.

OK; well... the closest to what I had in mind appears to be #6.
However, the caveat warning that #6 is difficult to prove
mathematically seems spurious to me.

If one can write ruby code that evades ruby's type system, it's
a bug in the language implementation. So it's like having a caveat
for C++ that says, the compiler is supposed to generate valid
assembly code for all valid input statements, but this is very
difficult to prove mathematically. . . . Yes, it may be difficult
to prove that there are no implementation bugs in C++ compilers,
but we don't go around adding asterisks to that effect when
talking about C++.

In Ruby, an object's type is essentially related to which set
of messages that object responds to at any given point in time.
Classes, in Ruby are more useful for implementation inheritance,
than in establishing an object's type. Ruby is much more dynamic
than that; a given object need not necessarily respond to all
the methods provided by its superclass (it could undefine some
or all of those methods, for example.)

In Ruby, variables don't have types, objects do. And an object's
type cannot necessarily be usefully determined by looking at
the object class's inheritance hierarchy. Nonetheless, one
cannot evade Ruby's type system as one can in C++:

class Dog { public: void bark(); };
class Cat { public: void meow(); };
Dog *dog = new Dog();
((Cat *) dog)->meow();

In C++, the above results in undefined behavior. (Possibly crash.)
The equivalent in Ruby does NOT result in undefined behavior.

class Dog; def bark; puts "Woof!"; end; end
class Cat; def meow; puts "Mew!"; end; end

cat = Dog.new
cat.meow

NoMethodError: undefined method `meow' for #<Dog:0x2c6cf40>

In Ruby, we have merely tried to send an object a message it
doesn't respond to. That is a perfectly well-defined scenario
in the language, and the type system has not been violated or
evaded.

If it helps clarify, the following statements:

cat = Dog.new
cat.meow

...are semantically equivalent to:

some_object = Dog.new
some_object.send:)meow)

...In the latter, it should be clear that we are merely
attempting to send an object referenced by the variable
<some_object> a particular message, :meow.

Ultimately, it's really a fairly elegant and beautiful and
simple type system: Messages are sent to objects, which either
respond to them or they don't.

Sadly, I'm no math whiz - but "objects respond to messages or
they don't" doesn't strike me as likely to be as hard to
prove mathematically as #6 on the wiki page tries to claim.

One more example...

What should we call the 'type' of the following object?
=> #<Psychotic:0x279af38>

Here, we have an object that will happily respond to ANY method
called on it...
Awesome! I'm so happy you called my method + !

...EXCEPT methods of exactly six letters:
NoMethodError: I don't like six-letter method names, such as hooray!
from (irb):46:in `method_missing'
from (irb):55
from :0

What is the type of that object? (I'm not sure what to call its type,
but nevertheless its type still can't be disabled or evaded!)


Obviously not, I have also seen a few sizable systems entirely built
in PHP or Perl or whatever,
after they started out as a "pilot" and later they didnt have the
funds to rebuild them,
decided to go live with what they had, and kept expanding on it...

But how easy and fun are they to maintain and extend, when over the
years the
original creators leave the project and new people join, etc?

In 2.5 decades of programming so far, the two worst codebases
I've had to maintain so far were in C, and Java. I wouldn't
blame that language for that, just the coders.


Regards,

Bill
 
R

Rick DeNatale

At least, the Wikipedia entry for Ruby is not the right place to
describe the definition of type-safety and strong typing, since same
thing happens on language with similar type concept, like Python,
Lisp, or Smalltalk. They should have their own entries if they didn't
have yet.

At the very least that section should be moved from the main article
to the discussion page, it strikes me as violating wikipedias NPOV
policy.

I'd also argue that this whole issue of "type safety" should be
abstracted a bit to talk about safety in general. My experience is
that those who demand "type safety" are concerned about the type of
bugs which happen when executable code which depends on assumptions
about the format of the date it operates on which is bound at compile
time encounters data which violates those assumptions. highly "type
safe" languages like C++ bind a lot of such assumptions into the
executable code, Java less so, Smalltalk less than that, and Ruby the
least of the four.

http://talklikeaduck.denhaven2.com/articles/2008/02/08/whose-variable-is-it-anyway

The difference it seems to me is that at one end the philosophy is
"check the credentials at the door, and if they pass, let whatever
happens happen", and at the other end, it's "trust but verify."

Trust but verify is actually safer in the end because it tends to keep
really bad things from happening when the credentials are confused or
forged, albeit at some cost of overhead. It also provides more
flexibility and does away with the programmer overhead of "high
ceremony" languages like C++ and Java.

And years of development of dynamic languages like Lisp, Smalltalk,
and Self have shown how to minimize, or even eliminate in some cases,
the overhead of late binding.
 
T

Todd Benson

Hi,

I added some paragraphs to the Ruby article on Wikipedia:

Type system
- Is Ruby type-safe?
- Is Ruby strongly typed?

Criticism
- Lack of variable declarations
- Dynamic typing
- Overloaded operators
- Reflective and meta-programming features

Some "peer review" appreciated :)

http://tinyurl.com/fpwes

Please feel free to improve upon it, or post comments in this thread.

Greetings,

The concept that water is fluid but can only either erode rock over a
long period or infiltrate and then break it quickly by freezing comes
to mind.

Just a thought.

Small rant ahead...

I've been concerned about using Ruby meta-programming features for
some time. People talk about fast development and then go on about
the requirement for lengthy unit tests; brag about them, even. We're
talking about tests that cannot catch every edge case in a complicated
application. Mix that with rigid standards/conventions in a
development environment -- another requirement, all in the interest of
productivity, of course -- and the turn around for product is better,
but not by much. Not to mention maintenance/support/refactoring,
which can be costly in hours. The same goes, it seems, for other
dynamic-based languages.

I'd like someday to see a long-based economic study for a business
adapting Ruby in lieu of, say, Java (I really don't like Java that
much btw :)

Todd
 
P

Phlip

Rick said:
At the very least that section should be moved from the main article
to the discussion page, it strikes me as violating wikipedias NPOV
policy.

The discussion page is for discussing the WP article itself - not for discussing
the target of the article.

Todd said:
I've been concerned about using Ruby meta-programming features for
some time. People talk about fast development and then go on about
the requirement for lengthy unit tests; brag about them, even.

Lengthy unit tests do not catch edge-case problems - in any language.

The test cases required to avoid endless debugging, under a dynamic language,
are remarkably short - often shorter than for a statically typed language.

The test cases don't match each type-check. They actually do the exact opposite.
They help absolve the need for any type checking. Program behavior can emerge
from mixing and matching its objects, without obsessing about each one's exact type.
I'd like someday to see a long-based economic study for a business
adapting Ruby in lieu of, say, Java (I really don't like Java that
much btw :)

I work at a successful, profitable Ruby shop which will not go back to Java.
'Nuff said!
 
E

Eric Mahurin

[Note: parts of this message were removed to make it a legal post.]

At the very least that section should be moved from the main article
to the discussion page, it strikes me as violating wikipedias NPOV
policy.

Exactly. In addition to the highly biased comments about typing, you'll see
the same thing about operator overloading and reflective/meta programming.
I'm guessing that whoever wrote these comments came from Java since it is
statically typed and is against operator overloading. These comments also
came from someone who has little experience with Ruby. I'd call this
troll-like behavior.
 
U

uusitaso

From: "Rule.rule" <[email protected]>








OK; well... the closest to what I had in mind appears to be #6.
However, the caveat warning that #6 is difficult to prove
mathematically seems spurious to me.

If one can writerubycode that evadesruby'stype system, it's
a bug in the language implementation. So it's like having a caveat
for C++ that says, the compiler is supposed to generate valid
assembly code for all valid input statements, but this is very
difficult to prove mathematically. . . . Yes, it may be difficult
to prove that there are no implementation bugs in C++ compilers,
but we don't go around adding asterisks to that effect when
talking about C++.

InRuby, an object's type is essentially related to which set
of messages that object responds to at any given point in time.
Classes, inRubyare more useful for implementation inheritance,
than in establishing an object's type. Rubyis much more dynamic
than that; a given object need not necessarily respond to all
the methods provided by its superclass (it could undefine some
or all of those methods, for example.)

InRuby, variables don't have types, objects do. And an object's
type cannot necessarily be usefully determined by looking at
the object class's inheritance hierarchy. Nonetheless, one
cannot evadeRuby'stype system as one can in C++:

class Dog { public: void bark(); };
class Cat { public: void meow(); };
Dog *dog = new Dog();
((Cat *) dog)->meow();

In C++, the above results in undefined behavior. (Possibly crash.)
The equivalent inRubydoes NOT result in undefined behavior.

class Dog; def bark; puts "Woof!"; end; end
class Cat; def meow; puts "Mew!"; end; end

cat = Dog.new
cat.meow

NoMethodError: undefined method `meow' for #<Dog:0x2c6cf40>

InRuby, we have merely tried to send an object a message it
doesn't respond to. That is a perfectly well-defined scenario
in the language, and the type system has not been violated or
evaded.

If it helps clarify, the following statements:

cat = Dog.new
cat.meow

..are semantically equivalent to:

some_object = Dog.new
some_object.send:)meow)

..In the latter, it should be clear that we are merely
attempting to send an object referenced by the variable
<some_object> a particular message, :meow.

Ultimately, it's really a fairly elegant and beautiful and
simple type system: Messages are sent to objects, which either
respond to them or they don't.

Sadly, I'm no math whiz - but "objects respond to messages or
they don't" doesn't strike me as likely to be as hard to
prove mathematically as #6 on the wiki page tries to claim.

One more example...

What should we call the 'type' of the following object?


=> #<Psychotic:0x279af38>

Here, we have an object that will happily respond to ANY method
called on it...


Awesome! I'm so happy you called my method hello !>> x.lets_be_friends

Awesome! I'm so happy you called my method lets_be_friends !>> x + x

Awesome! I'm so happy you called my method + !

..EXCEPT methods of exactly six letters:


NoMethodError: I don't like six-letter method names, such as hooray!
from (irb):46:in `method_missing'
from (irb):55
from :0

What is the type of that object? (I'm not sure what to call its type,
but nevertheless its type still can't be disabled or evaded!)





In 2.5 decades of programming so far, the two worst codebases
I've had to maintain so far were in C, and Java. I wouldn't
blame that language for that, just the coders.

Regards,

Bill

Thank you very much, Bill.
Most worthwhile input, and just the kind of feedback I was hoping for
when I started this thread :)

Anyways, I think I had a realization here -- that a type-identifier in
any earlier language I have seen, are considered to be a Constant.
Once it's defined, it's defined and doesn't change.
While a type-identifier (or class-identifier) in Ruby should rather be
looked upon as a Variable.
Just as an ordinary variable, it can mean different things in
different places within a scope (or even within an expression, I
guess).

That is certainly a new dimension, and obviously would require quite
different frameworks for mathematical/logical analysis, for example.

As a trivial example,
if you have a function F that takes an argument of type A and returns
a result of type A,
well, actually the type A may have changed between the point of
invocation of the function and the point where the result is returned.

Hmmm... by meta-programming, actually, the function F may also have
changed during the call, I guess...
OK, will digest this for a while... :)

Best regards,
 
R

Rule.rule

From: "Rule.rule" <[email protected]>








OK; well... the closest to what I had in mind appears to be #6.
However, the caveat warning that #6 is difficult to prove
mathematically seems spurious to me.

If one can writerubycode that evadesruby'stype system, it's
a bug in the language implementation. So it's like having a caveat
for C++ that says, the compiler is supposed to generate valid
assembly code for all valid input statements, but this is very
difficult to prove mathematically. . . . Yes, it may be difficult
to prove that there are no implementation bugs in C++ compilers,
but we don't go around adding asterisks to that effect when
talking about C++.

InRuby, an object's type is essentially related to which set
of messages that object responds to at any given point in time.
Classes, inRubyare more useful for implementation inheritance,
than in establishing an object's type. Rubyis much more dynamic
than that; a given object need not necessarily respond to all
the methods provided by its superclass (it could undefine some
or all of those methods, for example.)

InRuby, variables don't have types, objects do. And an object's
type cannot necessarily be usefully determined by looking at
the object class's inheritance hierarchy. Nonetheless, one
cannot evadeRuby'stype system as one can in C++:

class Dog { public: void bark(); };
class Cat { public: void meow(); };
Dog *dog = new Dog();
((Cat *) dog)->meow();

In C++, the above results in undefined behavior. (Possibly crash.)
The equivalent inRubydoes NOT result in undefined behavior.

class Dog; def bark; puts "Woof!"; end; end
class Cat; def meow; puts "Mew!"; end; end

cat = Dog.new
cat.meow

NoMethodError: undefined method `meow' for #<Dog:0x2c6cf40>

InRuby, we have merely tried to send an object a message it
doesn't respond to. That is a perfectly well-defined scenario
in the language, and the type system has not been violated or
evaded.

If it helps clarify, the following statements:

cat = Dog.new
cat.meow

..are semantically equivalent to:

some_object = Dog.new
some_object.send:)meow)

..In the latter, it should be clear that we are merely
attempting to send an object referenced by the variable
<some_object> a particular message, :meow.

Ultimately, it's really a fairly elegant and beautiful and
simple type system: Messages are sent to objects, which either
respond to them or they don't.

Sadly, I'm no math whiz - but "objects respond to messages or
they don't" doesn't strike me as likely to be as hard to
prove mathematically as #6 on the wiki page tries to claim.

One more example...

What should we call the 'type' of the following object?


=> #<Psychotic:0x279af38>

Here, we have an object that will happily respond to ANY method
called on it...


Awesome! I'm so happy you called my method hello !>> x.lets_be_friends

Awesome! I'm so happy you called my method lets_be_friends !>> x + x

Awesome! I'm so happy you called my method + !

..EXCEPT methods of exactly six letters:


NoMethodError: I don't like six-letter method names, such as hooray!
from (irb):46:in `method_missing'
from (irb):55
from :0

What is the type of that object? (I'm not sure what to call its type,
but nevertheless its type still can't be disabled or evaded!)





In 2.5 decades of programming so far, the two worst codebases
I've had to maintain so far were in C, and Java. I wouldn't
blame that language for that, just the coders.

Regards,

Bill

Thank you very much, Bill.
Most worthwhile input, and just the kind of feedback I was hoping for
when I started this thread :)

Anyways, I think I had a realization here -- that a type-identifier in
any earlier language I have seen, are considered to be a Constant.
Once it's defined, it's defined and doesn't change.
While a type-identifier (or class-identifier) in Ruby should rather be
looked upon as a Variable.
Just as an ordinary variable, it can mean different things in
different places within a scope (or even within an expression, I
guess).

That is certainly a new dimension, and obviously would require quite
different frameworks for mathematical/logical analysis, for example.

As a trivial example,
if you have a function F that takes an argument of type A and returns
a result of type A,
well, actually the type A may have changed between the point of
invocation of the function and the point where the result is returned.

Hmmm... by meta-programming, actually, the function F may also have
changed during the call, I guess...
OK, will digest this for a while... :)

Best regards,
 
T

ThoML

While a type-identifier (or class-identifier) in Ruby should rather be
looked upon as a Variable.

Actually a class rather can be thought of as an object assigned to a
constant (if I'm not mistaken):

irb(main):002:0> class A
irb(main):003:1> end
=> nil
irb(main):004:0> A.class
=> Class
irb(main):006:0> A.superclass
=> Object
if you have a function F that takes an argument of type A and returns
a result of type A,
well, actually the type A may have changed between the point of
invocation of the function and the point where the result is returned.

You mustn't forget that a ruby program isn't a closed system where
every
part is defined at compile time. If you remove eval & friends, you
could
perform some whole system analysis and to some extent formally prove
the
correctness of a program, I think. This isn't really news though.
 
A

Alexey Verkhovsky

But more importantly a general problem with just saying "test your code",
is that most real-life applications have an infinite number of
possible different inputs,
and you can only test a finite number of input-cases

And all that static typing does for this line of argument is to
restrict the number of input cases to a somewhat smaller infinity. And
not even in a way that excludes interesting defects, either.

Empirically, those who develop in dynamic languages commonly observe two things:

1. Use of static typing (at least, as implemented by current
mainstream static languages) causes accidental complexity. Complexity
== bugs.

2. Software written in Python or Ruby does not appear to be more
defect-prone in production, than software written in Java or C#.
-- one would like to ensure every code-line is executed at least once during every test.
Already this goal alone, is far from a trivial task in real-life applications.

In practice, nobody should ever rely on automated tests alone, anyway.
And gunning for 100% code coverage by automated tests is waste,
because automated tests have their cost. For some parts of the
application code that cost doesn't justify the benefits.

By the way, decent code coverage is far easier to achieve with dynamic
typing and open classes. Mocking out awkward integration boundaries is
a breeze, even when those integration boundaries are third-party and
not designed for testability.
I think Ruby is a very interesting and fun tool to use and experiment
with, but might still not be the totally ideal one for every possible task
and application on earth,

Oh, well... show me such a tool, and I will forget about Ruby in a
heartbeat. Pending that, for a pretty wide set of applications, Ruby
sucks the least.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top