MQL equivalent to Python's None type? - page 4

 
Amir Yacoby:
I am not going to define a dummy CObject just for calling those methods. 
It's for OO only.

You edited... 


I'm not sure what you are talking about. This is a misunderstanding. The none.mqh would go into your include dir (or stdlib) and you don't even call the class at all, the only reason I use it is to protect the global namespace. As far as anyone is concerned, NONE(type) is the only thing available to call. 


#include<none.mqh>

string g_string = NONE(string);

void OnInit()
{
   if(g_string == NONE(string))
      . . .
}
 
nicholi shen:

That's the entire point of this thread. No matter what scope, you should have access to a universal None type of which you can use to initialize any type in order to compare to the same None value. This, of course, is used to avoid collisions with ambiguous values. For example, 


Ok, if you write mixed code - that's ok and stick to your solution (you could also made None() a function and not a class then). 
I don't write mixed code, and I avoid static classes, although this class is stateless.. but still I think it's better off.

Also if you fear about other future None methods - you could define those in CObject with virtual and final like:

virtual int None(int) const final {return INT_MAX;}

which will prevent any future decendant named None() anywhere in your classes.

Of course it comes with a price, I am not suggesting to do this.. just a possibility.

 
nicholi shen:

You edited... 


I'm not sure what you are talking about. This is a misunderstanding. The none.mqh would go into your include dir (or stdlib) and you don't even call the class at all, the only reason I use it is to protect the global namespace. As far as anyone is concerned, NONE(type) is the only thing available to call. 


Yes, I got you. I just prefer not using static classes, but it's just a preference. So, everyone should choose his own style. 

 
Amir Yacoby:

Ok, if you write mixed code - that's ok and stick to your solution (you could also made None() a function and not a class then). 
I don't write mixed code, and I avoid static classes, although this class is stateless.. but still I think it's better off.

Also if you fear about other future None methods - you could define those in CObject with virtual and final like:

which will prevent any future decendant named None() anywhere in your classes.

Of course it comes with a price, I am not suggesting to do this.. just a possibility.

I honestly have no idea what you are talking about... I feel like you are missing the point of this implementation of the equivalent python-None. 

Let's start over so we can be on the same page, cool?

The sole value of the type NoneTypeNone is frequently used to represent the absence of a value

So in MQL the typical convention is to initialize a variable using one of many defined constants. e.g.

double val = DBL_MIN;
double val = DBL_MAX;
double val = NULL;
double val = WRONG_VALUE;

 ...

string mssg = NULL;
string mssg = "";
string mssg = "__string_null__";

 


So let's quickly address null in MQL. Null is a value, while a variable set at its maximum edge represents the absence of value

double val = DBL_MIN; //Set to edge of constraint

One problem arises when developer-error leads to the wrong comparison for absence of value. 

if(val == DBL_MAX) ///ERROR! val was set to DBL_MIN

A quick fix to this is to define a None value for each type which get's used in ALL cases where a double variable needs to be initialized. 

#define NONE_DBL DBL_MAX

double val = NONE_DBL;
if(val == NONE_DBL) //No chance of messing this up!

This simple solution would be ideal with one major exception, templates! Consider the following template function. 

template <typename T>
int array_resize_init_none(T &array[], int size)
{
   int total = ArrayResize(array, size);
   for(int i=0; i<total; i++){
      array[i] = NULL;
   }
   return total;
}

Remember earlier we agreed that NULL in MQL resolves to a value (usually 0). We don't want to initialize our array of absent values with actual values (NULL). The only option is to make a dozen overloaded functions, one for each type. 

int array_resize_init_none(double &array[], int size)
{
   int total = ArrayResize(array, size);
   for(int i=0; i<total; i++){
      array[i] = NONE_DBL;
   }
   return total;
}

int array_resize_init_none(int &array[], int size)
{
   int total = ArrayResize(array, size);
   for(int i=0; i<total; i++){
      array[i] = NONE_INT;
   }
   return total;
}
...and on...
...and on...........

You could do it the hard way... or.... you could use the NONE abstraction that I've proposed. The template function remains simple and resolves the correct None value no matter what type is passed to the template. 

template <typename T>
int array_resize_init_none(T &array[], int size)
{
   int total = ArrayResize(array, size);
   for(int i=0; i<total; i++){
      array[i] = NONE(T);
   }
   return total;
}


Hopefully we're on the same page to this point. Now let's talk about the decision to use a class instead of functions. Most programmers consider it a sloppy and lazy practice to clutter the global namespace with functions that aren't ever intended to be called. In this specific case, there is absolutely no value in cluttering global namespace with a dozen overloaded functions that are exclusively intended to be called via a proxy function. You, the end user, are not suppose to call these functions, thus, the implementation is cleaner when hidden inside of a private class. If you like to clutter your global namespace with stuff that doesn't belong there then that's you prerogative, but in this case I am following programming best practices. I hope that cleared things up. 

 
nicholi shen:

Hopefully we're on the same page to this point. Now let's talk about the decision to use a class instead of functions. Most programmers consider it a sloppy and lazy practice to clutter the global namespace with functions that aren't ever intended to be called. In this specific case, there is absolutely no value in cluttering global namespace with a dozen overloaded functions that are exclusively intended to be called via a proxy function. You, the end user, are not suppose to call these functions, thus, the implementation is cleaner when hidden inside of a private class. If you like to clutter your global namespace with stuff that doesn't belong there then that's you prerogative, but in this case I am following programming best practices. I hope that cleared things up. 


Well, I don't know why you think I did not understand that.. and because your introductory part is obvious, I will just respond to this last part.
I take it that by talking about class vs. function - you talk about your implementation into a static class, which is better then overloaded functions. I can agree, except I dislike static classes, which I take them to be anti patterns (not quite in this case because the None class is stateless).

So, I don't have any problems with your implementation.
I just prefer putting those in protected section of CObject. 

*and btw, you can put them in CObject private section, and in the protected just put:

protected:
   template<typename T>
   T        None() const {return None((NULL)T);}
As you did in you case - so the user will have just one method to call. 
 
Amir Yacoby:

Well, I don't know why you think I did not understand that.. and because your introductory part is obvious, I will just respond to this last part.
I take it that by talking about class vs. function - you talk about your implementation into a static class, which is better then overloaded functions. I can agree, except I dislike static classes, which I take them to be anti patterns (not quite in this case because the None class is stateless).

So, I don't have any problems with your implementation.
I just prefer putting those in protected section of CObject. 

*and btw, you can put them in CObject private section, and in the protected just put:

As you did in you case - so the user will have just one method to call. 

No, respectfully, you can't. The code belongs in the global namespace and not nested in the base class. If you put it in the base class you cannot call it unless it's from a subclass, and that doesn't make any sense.


But just so everyone is happy I've updated the None library to use either syntax. 

#include <None.mqh>
//Tests
void OnStart(){
   Print(NONE(string)   == NONE_STRING);
   Print(NONE(double)   == NONE_DOUBLE);
   Print(NONE(int)      == NONE_INT);
   Print(NONE(uint)     == NONE_UINT);
   Print(NONE(long)     == NONE_LONG);
   Print(NONE(ulong)    == NONE_ULONG);
   Print(NONE(char)     == NONE_CHAR);
   Print(NONE(uchar)    == NONE_UCHAR);
   Print(NONE(short)    == NONE_SHORT);
   Print(NONE(ushort)   == NONE_USHORT);
   Print(NONE(color)    == NONE_COLOR);
   Print(NONE(datetime) == NONE_DATETIME);
   Print(NONE(CObject*) == NONE_POINTER);
}
Files:
None.mqh  2 kb
 
nicholi shen:

No, respectfully, you can't. The code belongs in the global namespace and not nested in the base class. If you put it in the base class you cannot call it unless it's from a subclass, and that doesn't make any sense.


But just so everyone is happy I've updated the None library to use either syntax. 

It's your opinion, which does not make it true. I don't need to call it from any other place apart from it's subclass. 

Can you think of a subclass of CObject that needs to call the None() of another CObject? I guess no. So, there you have it.

And because I don't write mixed oo with non oo - I need it just from within a class. 
 
Amir Yacoby:
It's your opinion, which does not make it true. I don't need to call it from any other place apart from it's subclass. 

Can you think of a subclass of CObject that needs to call the None() of another CObject? I guess no. So, there you have it.

And because I don't write mixed oo with non oo - I need it just from within a class. 

Ok.. well that's your very specific use-case. Everyone else needs to initialize variables and/or return 'None' values from any scope.  

 
nicholi shen:

Ok.. well that's your very specific use-case. Everyone else needs to initialize variables and/or return error values from any scope.  

I see you did a quick extensive poll :)


It's ok, I don't have an argue with you, you seem to want to be right. I let everyone choose, without saying which is better, just the points I find about each. Everyone should decide for themselves, if they even want to use one of those.

Good night, Nocholi. 

And btw - I don't take your credit for phrasing the problem in the first place and thinking to solve it in a unified way. It's very bright idea you had, and you phrased the problem correctly - which is 90 percent of the solution. from there on, there might be more then one options for different people.
 
Amir Yacoby:

I see you did a quick extensive poll :)


It's ok, I don't have an argue with you, you seem to want to be right. I let everyone choose, without saying which is better, just the points I find about each. Everyone should decide for themselves, if they even want to use one of those.

Good night, Nocholi. 

I am right. 


There is a need for the standardization of the representation of absent of values (henceforth referred to as "None"). Currently, the way to set a variable to None is to set it at the edge of its limit. (eg. double x = DBL_MAX) These vary greatly from type to type. Additionally there is currently no way to set None values using templates. This library solves these issues. If you want to continue using a plethera of MQL constants and making overloaded funcitons instead of using template then don't use it. Those who understand the concept of None will instantly see the value in having both; 1. new constants defined with prefix NONE_TYPE and 2. A macro which you can pass the type and receive the appropriate None value in return. Either can be used or None (pun intended), it's your choice.