MQL equivalent to Python's None type?

 

A major missing element in the MQL language is a universal None type like in Python. MQL5 has "WRONG_VALUE", but with double, for example, it sets the value to -1.0 which is unacceptable in most cases. It became an obvious issue when I was debugging a substantial project and come to find-out that a double was initialized to DBL_MIN but then later on accidentally compared DBL_MAX. This of course introduced a very difficult to find bug in the code. So I'm curious to how others are implementing safe 'None' value initialization and comparisons. Here's what I came up with, but I can't help but think that there might be a better way??? 


class _NoneType{
 public:
   static double  None(double _)     { return DBL_MAX;   }
   static int     None(int _)        { return INT_MAX;   }
   static uint    None(uint _)       { return UINT_MAX;  }
   static long    None(long _)       { return LONG_MAX;  }
   static ulong   None(ulong _)      { return ULONG_MAX; }
   static char    None(char _)       { return CHAR_MAX;  }
   static uchar   None(uchar _)      { return UCHAR_MAX; }
   static short   None(short _)      { return SHORT_MAX; }
   static ushort  None(ushort _)     { return USHORT_MAX;}
   static string  None(string _)     { return "__NULL-*-*-STRING__";}
   static color   None(color _)      { return clrNONE;   }
   static datetime None(datetime _)  { return -1;        }
};
#define NONE(T)  _NoneType::None((T)NULL)


   
void OnStart()
{
   //Unit tests
   #define TEST(V, T) printf("%s: %s", #T, (V == NONE(T) && typename(V) == #T)?"passed":"failed")
   Print("Unit tests for 'None' values.");
   int      a = NONE(int);       TEST(a, int);
   uint     b = NONE(uint);      TEST(b, uint);
   double   c = NONE(double);    TEST(c, double);
   long     d = NONE(long);      TEST(d, long);
   ulong    e = NONE(ulong);     TEST(e, ulong);
   short    f = NONE(short);     TEST(f, short);
   ushort   g = NONE(ushort);    TEST(g, ushort);
   char     h = NONE(char);      TEST(h, char);
   uchar    i = NONE(uchar);     TEST(i, uchar);
   color    j = NONE(color);     TEST(j, color);
   datetime k = NONE(datetime);  TEST(k, datetime);
   string   s = NONE(string);    TEST(s, string);
   
   //func test
   double res = double_test();
   if(res != NONE(double) && res > 0.0)
      Print("func test Failed");
   else
      Print("func test Passed");
}
//+------------------------------------------------------------------+
double double_test()
{
   if(false)
      return 1.0;
   return NONE(double);
}
 
Interesting question.
For double Ì return (double)"nan" and check with MathIsValidNumber(returned val)

I am not by editor to see exact syntax

* nan is short for Not A Number. it can be used to every int type as well if i remember correctly
 
nicholi shen:

A major missing element in the MQL language is a universal None type like in Python. MQL5 has "WRONG_VALUE", but with double, for example, it sets the value to -1.0 which is unacceptable in most cases. It became an obvious issue when I was debugging a substantial project and come to find-out that a double was initialized to DBL_MIN but then later on accidentally compared DBL_MAX. This of course introduced a very difficult to find bug in the code. So I'm curious to how others are implementing safe 'None' value initialization and comparisons. Here's what I came up with, but I can't help but think that there might be a better way??? 


I sometimes find myself in a similar situation, but it does not happen often (at least, in my experience).

I do not have a custom-built solution for this the same way you did. But I agree, nullable types, or a similar feature, would be nice to have. It can resolve some ambiguity in coding. For example, with SymbolInfoInteger(), I almost always use the second form nowadays (passing a variable by reference). The first form can be unreliable at times with no easy way to confirm if the value you received is the really the requested data or not. I think the same can also be said of other functions, such as ObjectFind(), which can be confusing if you forgot that the return value should be less than zero to confirm that no object was found.

 
The builtin solution for double type is "nan"
(Not a number)
 
Amir Yacoby:
Interesting question.
For double Ì return (double)"nan" and check with MathIsValidNumber(returned val)

I am not by editor to see exact syntax

* nan is short for Not A Number. it can be used to every int type as well if i remember correctly

It seems to work for double but (int)"nan" == 0. 

 
nicholi shen:

It seems to work for double but (int)"nan" == 0. 

For int usually the WRONG_VALUE is enough, but not perfect you right on that
 
Amir Yacoby:
For int usually the WRONG_VALUE is sometimes enough, but not perfect you right on that

My goal with this is to eliminate any possible ambiguity in setting and comparing values of 'None'. I realize I could simply re-define values

#define NONE_INT INT_MAX

int a = NONE_INT;

...but I prefer to see a more explicit use...

int a = NONE(int);

Thoughts?

 

Similar to @Enrico Lambino , I come across this situation sometimes but not really that often.

I basically do a lot of checking of return values on a case-by-case basis.

I do this through unit tests and/or asserts() in my code.

Yes, the asserts() make the code less efficient, but I am willing to do that rather than chase down round pegs in square holes. As with anything, one chooses their battles in life.

#define assert(condition, message, returnVal)                                                      \
    if(!(condition)) {                                                                             \
        alert(StringFormat("Assertion [%s] failed! %s", #condition, message ));                    \
        return returnVal;                                                                          \
    }

#define assertRemove(condition, message, returnVal)                                                \
    if(!(condition)) {                                                                             \
        alert(StringFormat("Assertion [%s] failed! %s", #condition, message ));                    \
        ExpertRemove();                                                                            \
        return returnVal;                                                                          \
    }
Oh, and in some cases, I will use a ridiculous value as a return value. Something like 999 when I expect a number between 0 an 10, e.g.
 
nicholi shen:

My goal with this is to eliminate any possible ambiguity in setting and comparing values of 'None'. I realize I could simply re-define values

...but I prefer to see a more explicit use...

Thoughts?

I think good phrasing of the problem and pretty good solution
 
Anthony Garot:

Similar to @Enrico Lambino , I come across this situation sometimes but not really that often.

I basically do a lot of checking of return values on a case-by-case basis.

I do this through unit tests and/or asserts() in my code.

Yes, the asserts() make the code less efficient, but I am willing to do that rather than chase down round pegs in square holes. As with anything, one chooses their battles in life.

Oh, and in some cases, I will use a ridiculous value as a return value. Something like 999 when I expect a number between 0 an 10, e.g.

This is cool! I don't know why but it never dawned on me to pass entire expressions into the MACRO. I present to you -- MQL's first anonymous functions. ;)


 

#define lambda(type, var, return_statement) \
class _Lambda{public:static type func(type var){return(type)(return_statement);}};

#define map(lambda_func, array) {\
   lambda_func\
   for(int i=ArraySize(array)-1; i>=0; --i){\
      array[i] = _Lambda::func(array[i]);\
   }\
}

void OnStart()
{
   int nums[] = {1, 2, 3, 4};
   map(lambda(int, x, pow(x, 2)), nums);  //square each num in array
   map(lambda(int, x, x - 5), nums);      //subtract 5 from the resulting nums in array
   ArrayPrint(nums); //-4  -1  4  11
   
   string hellos[] = {
      "Hello", "World!",
      "Hello", "World!"
   };
   map(lambda(string, x, x=="Hello"? x+" World!":"Hello "+x), hellos)
   ArrayPrint(hellos); //"Hello World!" "Hello World!" "Hello World!" "Hello World!" 
}
 
nicholi shen:

This is cool! I don't know why but it never dawned on me to pass entire expressions into the MACRO. I present to you -- MQL's first anonymous functions. ;)

You continue to bridge MQL to python!

Glad my off-topic rambling was helpful. :-D