0

I am developing a dynamically typed, interpreted programming language, which is interpreted by a runtime written in Java. As Java is statically typed, I need to define how the numbers used in the language are stored in the interpreter.

My initial idea was to use BigDecimal for all calculations, but this makes the code more complex and I assume has added overhead for the runtime. Another possibility is to parse the number, if it is a natural then use a long and if it a real number use a double.

So which solution is optimal, and why?

closed as off-topic by Ixrec, Robert Harvey, Blrfl, greyfade, user22815 May 12 '15 at 22:14

  • This question does not appear to be about software engineering within the scope defined in the help center.
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 2
    They have different optimal situations. I don't believe one can say which is optimal based on the existing information. – user40980 May 12 '15 at 19:26
  • 6
    Voting to close because this is either too broad/unspecified ("How should I implement numbers in this language I'm inventing?") or simply unanswerable because no single numeric type is ideal for everything. – Ixrec May 12 '15 at 19:31
  • 1
    @Ixrec I think your reason for closing is the answer to this question. Different number types exist because no one type is ideal for all situations. So choosing any one as THE number type of a language will limit that language to only be ideal in the same instances that number type is. That said, I'm not sure if this would be allowed as an answer since it doesn't answer the direct question but instead answers the underlying issue. – Lawtonfogle May 12 '15 at 19:35
  • @Lawtonfogle Yep, it's a question whose correct answer is essentially "your question is inherently unanswerable because it is based on a flawed premise". But that's probably not a useful answer, even if it is correct. The hope is that OP can change this into a more specific question like "I want my language to be good at X but I'm concerned about Q so would it be a good idea to use type Y in the implementation?" – Ixrec May 12 '15 at 19:40
  • @Ixrec But if the flawed premise is a simple and common one, wouldn't explaining it not be helpful? This isn't a case where answering the flawed premise would require a textbook (keyword being 'require' as I'm sure there is enough information about the topic to fill a small library if you wanted to get detailed). – Lawtonfogle May 12 '15 at 20:52
5

All the numeric types can represent different numbers and have different semantics. Stop thinking about implementation concerns for a second and do language design. What kind of numeric types should your language offer its users? (Note that most languages offer multiple types, and for good reasons.) Find the answer to that, then implement it.

  • ECMAScript only has one number type, which I find quite reasonable for a language that isn't specifically targeted at scientific computing. Unfortunately, ECMAScript chose a really bad number type as its single universal type. I think an arbitrary precision BigDecimal type is a rather sane choice. Most people type in decimal numbers, not binary numbers, so they simply don't understand why 1/10 has an infinite representation. And most people don't expect the result of adding two positive numbers to become negative. – Jörg W Mittag May 12 '15 at 19:38
  • However, I whole-heartedly agree. The OP should design his language to be sane, regular, powerful, expressive, orthogonal, small. And then, when the language is designed, he should figure out how best to implement it. Implementation details creeping into language design is typically really ugly, and becomes a problem at the latest, when your language becomes popular, and there starts to be more than one independent implementation. See the struggles of JRuby, Rubinius, IronRuby, Jython, IronPython, PHP.NET, Phalanger, IronPHP, Quercus, P8, HipHop, Perl.NET, etc. – Jörg W Mittag May 12 '15 at 19:44
  • @JörgWMittag You don't think distinguishing between integer (i.e. discrete) and "real" (continuous) values might be useful? In any case, I didn't say they absolutely have to have multiple types, just that the assumption in the question (that there should be exactly one) should be questioned. – user7043 May 12 '15 at 19:46
3

Why not both?

In an interpreter for a dynamically typed language, you will need a common representation for all values. For example, we might have a function that can return either a number or a dictionary. In JavaScript:

function foo(want_dict) {
  if (want_dict) return { value: 42 };
  else return 42;
}

var result = foo(someVariable);

So, how could a dynamic language represent the contents of the result variable? Since the variable could be either a number or a dictionary, the variable does not have a specific type. Instead, the type information lives in the value itself, often in form of a type ID. In C, a value might be represented as

typedef struct {
    unsigned type_id;
    void *value;
} Value;

In Java, we could use a similar class:

class Value {
  private TypeId type;
  private Object value;
  public Value(TypeId type, Object value) {
    this.type = type;
    this.value = value;
  }
  public TypeId type() { return type; }
  public Object value() { return value; }
}

We could then have various values such as

return new Value(TypeId.Int,  new Integer(42));
...
HashMap<String, Value> rawValue = new HashMap<>();
rawValue.put("value", new Value(TypeId.Int, new Integer(42)));
return new Value(TypeId.Dict, rawValue);

When we try to use such a value, we first have to dynamically check that all values have acceptable types. E.g. the interpreter's implementation of addition might look like this:

Value doAddition(Value a, Value b) {
  if (a.type() != TypeId.Int)
    throw ...;
  if (b.type() != TypeId.Int)
    throw ...;
  return new Value(TypeId.Int, (Integer) a.value() + (Integer) b.value());
}

Alternatively, we could leverage the dynamic typing features of Java itself, i.e. type information is encoded in an inheritance hierarchy.

interface Value<T> {
  public T value();
}

class MyInt implements Value<Integer> {
  private int value;
  public MyInt(int value) { this.value = value; }
  @Override public Integer value() { return value; }
}

class MyDict implements Value<Map<String, Value>> {
  private Map<String, Value> value;
  public MyInt(Map<String, Value> value) { this.value = value; }
  @Override public Map<String, Value> value() { return value; }
}

Now, we can create various values like

return new MyInt(42);
...
HashMap<String, Value> rawValue = new HashMap<>();
rawValue.put("value", new MyInt(42));
return new MyDict(rawValue);

With this encoding, an implementation of an addition operator would look like this:

Value doAddition(Value a, Value b) {
  if (!(a instanceof MyInt))
    throw ...;
  if (!(b instanceof MyInt))
    throw ...;
  return new MyInt(a.value() + b.value());
}

So, both encodings look very much the same, but I would prefer an explicit tag since it's a bit more flexible, and is not limited by Java's type erasure (on the downside, everything is an Object and needs to be cast).

With dynamic typing we necessarily have some kind of runtime type support available, so we can trivially support multiple numeric types. In one encoding, we just have to add a new type ID, in the other another subclass:

class MyRat implements Value<Double> {
  private double value;
  public MyRat(double value) { this.value = value; }
  @Override public Double value() { return value; }
}

We can also make our implementation of addition polymorphic, so that the same operator can handle both integers and doubles:

Value doAddition(Value a, Value b) {
  if ((a instanceof MyInt) && (b instanceof MyInt))
    return new MyInt(a.value() + b.value());
  if ((a instanceof MyRat) && (b instanceof MyRat))
    return new MyRat(a.value() + b.value());
  throw ...;
}

That wasn't so hard!

The difficult part is deciding:

  1. Which numeric types do we want to support in our language? Only floating point numbers? (Javascript) Only integral types? (B) Both? (most languages) Both, but as a single type and we switch to doubles if the number would otherwise overflow? (Perl)
  2. Which sizes to we want to support? Native sizes for efficiency? Arbitrary-precision numbers? A collection of different width types? Do we want to support unsigned types?

Most of these problems can be answered when you think about what numbers will be used for in the language. E.g. doubles are useless when indexing an array, since not every natural number has a representation as a double. Integers are useless for non-discrete domains such as physical measurements. Doubles are useless for currency calculations (which are discrete). Integer overflow is useless, but efficient. And so on. As the other answer points out, this is ends up being a question of language design, but it's not too difficult to implement any choice you arrive at.

Not the answer you're looking for? Browse other questions tagged or ask your own question.