Poll! What is the value of this constant?

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

What is the value of FILE_EXTENSION_PDF?

pdf
46
88%
.pdf
6
12%
 
Total votes: 52

heatsink
Posts: 86
Joined: Fri Jun 30, 2006 8:58 am UTC

Re: Poll! What is the value of this constant?

Postby heatsink » Tue Sep 09, 2014 2:56 pm UTC

korona wrote:Using a Null Object instead of null only makes sense if that object can provide a meaningful implementation of all methods. Using a Maybe/Optional/etc. return type makes sense when an empty result is part of the semantics of your function.

You say you disagree on almost every point, then you echo what the article is saying about alternatives to 'null'. I am confused. To reiterate, the article is saying that these two patterns subsume every useful way of using 'null' while excluding the broken ways of using it.

korona wrote:In general that talk about "domains of primitive data types are too large" contains a lot of bullshit.
So this function has 2^2 = 4 possible implementations. Perhaps we could write a test case for each one.

That is just totally stupid. I don't think I have to explain why.

Why is it stupid? Is it because a Boolean could be FileNotFound null, so there are actually 3 possible values? Is it because unaryBooleanOp(x) may throw an exception or save its argument to a file, so there are more than 3^3 possible function behaviors? Or perhaps you don't think that it's possible to write four test cases?

korona wrote:It is standard practice to use -1, 0, 1 return values for comparison operations. Doing something else certainly violates the principle of least surprise. It is as bad as the famous "true", "false", "file not found" enum.

That's fine until someone discovers that they need to handle unordered values like floating-point NaN, and decides to do it by introducing a "-2" return value meaning "the arguments are unordered." They rewrite 20% of the compare-based library code to handle "-2" correctly, because that's the least-effort way to make their test cases pass. They update comments in half of the functions that they have modified. Now the comments don't correctly describe the code and it is unclear how some of the library functions should handle a "-2" value. (Do I have to decide whether sorting should put unordered values at the beginning, or put them at the end, or remove them from the list, or throw an exception?) I've had to deal with the outcome of this kind of codebase evolution, where it is unclear what program behavior should be correct because various people have put code together without thinking through the consequences.

You probably think that introducing that "-2" value after the fact is a bad way to do things. If your explanation for why it's bad is based on domain modeling or code contracts, you're effectively saying we should pretend we have a three-valued Ordering type, even though we really don't. By this point, you're agreeing with a substantial part of what the article is saying.

korona
Posts: 495
Joined: Sun Jul 04, 2010 8:40 pm UTC

Re: Poll! What is the value of this constant?

Postby korona » Tue Sep 09, 2014 10:20 pm UTC

heatsink wrote:
korona wrote:Using a Null Object instead of null only makes sense if that object can provide a meaningful implementation of all methods. Using a Maybe/Optional/etc. return type makes sense when an empty result is part of the semantics of your function.

You say you disagree on almost every point, then you echo what the article is saying about alternatives to 'null'. I am confused. To reiterate, the article is saying that these two patterns subsume every useful way of using 'null' while excluding the broken ways of using it.

While I agree that those patterns subsume every useful way of using null I don't agree that plain null is not useful. I would prefer a findItem() function that returns an item if it exists and null otherwise to a function that returns a Maybe type; simply because that involves less syntactic overhead. Note that I'm only talking about imperative languages with relatively poor support for algebraic types, like Java or C++! If you want a function that is guaranteed to return an item you could always implement an additional findItemOrDie() function with fail-fast behavior.

heatsink wrote:
korona wrote:In general that talk about "domains of primitive data types are too large" contains a lot of bullshit.
So this function has 2^2 = 4 possible implementations. Perhaps we could write a test case for each one.

That is just totally stupid. I don't think I have to explain why.

Why is it stupid? Is it because a Boolean could be FileNotFound null, so there are actually 3 possible values? Is it because unaryBooleanOp(x) may throw an exception or save its argument to a file, so there are more than 3^3 possible function behaviors? Or perhaps you don't think that it's possible to write four test cases?

It is stupid because the test cases will require more code and more time to implement than the actual code. Because the function is that simple (only 4 possible implementations) it can be implemented by a one line expression. It is stupid to write four test cases which require at least an additional file and manipulation of the build configuration to test a simple one line function.

heatsink wrote:
korona wrote:It is standard practice to use -1, 0, 1 return values for comparison operations. Doing something else certainly violates the principle of least surprise. It is as bad as the famous "true", "false", "file not found" enum.

That's fine until someone discovers that they need to handle unordered values like floating-point NaN, and decides to do it by introducing a "-2" return value meaning "the arguments are unordered." They rewrite 20% of the compare-based library code to handle "-2" correctly, because that's the least-effort way to make their test cases pass. They update comments in half of the functions that they have modified. Now the comments don't correctly describe the code and it is unclear how some of the library functions should handle a "-2" value. (Do I have to decide whether sorting should put unordered values at the beginning, or put them at the end, or remove them from the list, or throw an exception?) I've had to deal with the outcome of this kind of codebase evolution, where it is unclear what program behavior should be correct because various people have put code together without thinking through the consequences.

You probably think that introducing that "-2" value after the fact is a bad way to do things. If your explanation for why it's bad is based on domain modeling or code contracts, you're effectively saying we should pretend we have a three-valued Ordering type, even though we really don't. By this point, you're agreeing with a substantial part of what the article is saying.

Of course we should have a three-valued ordering type. Either put NaN at the minimum or at the maximum of that order. As you said, a four-valued ordering type is not useful at all; sorting functions cannot deal with it in a meaningful way. I don't really see a problem here or how that contradicts my points.

heatsink
Posts: 86
Joined: Fri Jun 30, 2006 8:58 am UTC

Re: Poll! What is the value of this constant?

Postby heatsink » Thu Sep 18, 2014 3:05 pm UTC

korona wrote:I would prefer a findItem() function that returns an item if it exists and null otherwise to a function that returns a Maybe type; simply because that involves less syntactic overhead. Note that I'm only talking about imperative languages with relatively poor support for algebraic types, like Java or C++!

I think the article is arguing in two parts: first, that type theory and algebraic data types are useful tools for reasoning about programs, and second, that programming languages should be used to automate this reasoning. I can understand that Java makes optional types more trouble than they're worth. I would still want them to be part of the design (the first part of the argument), even if they're not in the code (the second part). It reduces the tendency to have complicated specifications like "foo must be null if X; it must not be null if Y; otherwise it may or may not be null; i'm pretty sure that X && Y can't happen".

korona wrote:It is stupid because the test cases will require more code and more time to implement than the actual code. Because the function is that simple (only 4 possible implementations) it can be implemented by a one line expression. It is stupid to write four test cases which require at least an additional file and manipulation of the build configuration to test a simple one line function.

I think the point was that exhaustive testing is possible. It's emphasizing how types can statically exclude unwanted behavior, leaving less to be checked by testing or code review or whatever.

korona wrote:Of course we should have a three-valued ordering type. Either put NaN at the minimum or at the maximum of that order. As you said, a four-valued ordering type is not useful at all; sorting functions cannot deal with it in a meaningful way. I don't really see a problem here or how that contradicts my points.

Maybe. If the rest of the program requires that a sorted array is sorted in ascending order by the (<=) relation, that is, a[i] <= a[i+1] for all i, then no ordering will work. NaN is not less than, equal to, or greater than any value. If you redefine NaN to be the least or greatest value, you break other arithmetic properties that the program may also be relying on. In this case, it’s probably better to set the domain of array elements to be Float - {NaN}, and throw an exception if that's violated.

My point being that lots of implementation decisions are naturally settled by deciding what range of values is meaningful for the problem, then reflecting that decision in the code.

User avatar
Yakk
Poster with most posts but no title.
Posts: 11092
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Poll! What is the value of this constant?

Postby Yakk » Thu Sep 18, 2014 3:40 pm UTC

Note that I'm only talking about imperative languages with relatively poor support for algebraic types, like Java or C++!

Hey, speak for Java. optional<T> works perfectly fine in C++ with little syntactic overhead at point of use.

boost-like::variant<A,B,C> is a bit messier, but still quite usable in C++. I wish automatic dispatching to a set of function overloads was less messy (you need to create a function object: this is easier with C++14 via `auto&&` lambdas).

Taking the algebraic data type stuff (which is an int, double or std::string), and calling foo with the appropriate overload, looks like this:

Code: Select all

typedef notboost::variant<int,double,std::string> stuff;
stuff data;
bool worked = data.apply( [&](auto&& value)->bool {
  return foo(17, value);
} );

The ideal interface would be:

Code: Select all

typedef notboost::variant<int,double,std::string> stuff;
stuff data;
foo(17, data);

which is tricky to do in C++. If we have overload set objects (objects that represent the entire overload set of the token foo) (which I will denote @foo below) we could:

Code: Select all

typedef notboost::variant<int,double,std::string> stuff;
stuff data;
apply(@foo)(17, data);

with some work elsewhere, which isn't bad. (as variant is a type, not a meta-type, in C++, distinguishing between calling foo directly with the value data, and calling foo with the value inside data, requires the apply() or something equivalent).

Shorthand to extract the overload set of a function name is something I'd look forward to: there are some proposals. You can fake it to some extent with:

Code: Select all

#define OS(FUNC_NAME) ([&](auto&&...args)->decltype(FUNC_NAME(std::forward<decltype(args)>(args)...)){return FUNC_NAME(std::forward<decltype(args)>(args)...);})

where `OS(foo)` behaves like `@foo` above mostly. (we create a variardic lambda with SFINAE support that forwards its arguments to calling FUNC_NAME with the arguments)
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
Xeio
Friends, Faidites, Countrymen
Posts: 5099
Joined: Wed Jul 25, 2007 11:12 am UTC
Location: C:\Users\Xeio\
Contact:

Re: Poll! What is the value of this constant?

Postby Xeio » Thu Sep 18, 2014 4:15 pm UTC

Yakk wrote:Hey, speak for Java. optional<T> works perfectly fine in C++ with little syntactic overhead at point of use.
My main curiosity, is what does Option or similar types bring to the table that isn't already provided by null? Granted, I'm thinking more in C# terms, but "x != null" is equivalent to "x.isPresent()". "x.get()" still fails if x is missing and you call it without checking, so the 'null' problem still exists...

Though I suppose some of the functions provided in Java for Option<T> are more specific to the language. C# has some built-in language features like nullable value types, the null coalescing operator, and (soon) null propagation operators which make most of those obsolete.

Or is this just really all about the Option type forcing the developer to consider missing values? Not that I don't think that's a worthy goal, but shouldn't we just be adding compiler warnings when you don't check for null or something...?

User avatar
Yakk
Poster with most posts but no title.
Posts: 11092
Joined: Sat Jan 27, 2007 7:27 pm UTC
Location: E pur si muove

Re: Poll! What is the value of this constant?

Postby Yakk » Thu Sep 18, 2014 6:38 pm UTC

In C++, you can have both value and reference types, which can be optional.

You can distinguish between an optional pointer, and a pointer provided that is null, in some cases. Or you could write your optional to exclude null values (it becomes empty if you store null) if you prefer that semantics.

While everything in C#/Java is optional, because you can turn anything into null, there is a difference between "I want a value, and if you fail to give it to me, I'll throw, because it is against my contract" and "I don't care if you give me this value". optional means "I don't care" on an input parameter, and explicitly tells the caller that they should expect "nothing" on return sometimes. (again, C#/Java forces that possibility on almost every interface, which is a pain for programmers in that language, but hey). If you have literal types (say, C# structs or ints), you can stuff them into an optional (hopefully without the overhead of boxing).

In C++, an optional<foo> is different than a foo* or smart_ptr<foo> in that it is the actual value, not a reference/pointer to said value.

For UI that reflects something like selection, you really want at least 3 states -- nothing, ambiguous, and a value. This can be implemented as an extension of optional to a 3 state system instead of a 2 state system. Such values can easily be accumulated over the selection (if the thing selected has a value, and we currently have nothing, set that to the value. If the thing selected has a value, and we have a value, compare -- if different, go ambiguous, otherwise remain unchanged. If we are ambiguous, don't bother reading the value) pretty easily, then turned into UI equally easily. But that is a more generic algebraic type.
One of the painful things about our time is that those who feel certainty are stupid, and those with any imagination and understanding are filled with doubt and indecision - BR

Last edited by JHVH on Fri Oct 23, 4004 BCE 6:17 pm, edited 6 times in total.

User avatar
Xeio
Friends, Faidites, Countrymen
Posts: 5099
Joined: Wed Jul 25, 2007 11:12 am UTC
Location: C:\Users\Xeio\
Contact:

Re: Poll! What is the value of this constant?

Postby Xeio » Thu Sep 18, 2014 7:19 pm UTC

Yakk wrote:You can distinguish between an optional pointer, and a pointer provided that is null, in some cases. Or you could write your optional to exclude null values (it becomes empty if you store null) if you prefer that semantics.
Hrmmm, maybe. I'd be very skeptical of the clarity of a library having two different behaviors for "null" and "not provided", but that could just be coming from the codebases I've currently worked with. I'm sure there's some language/library out there where that would be idiomatic.

Yakk wrote:While everything in C#/Java is optional, because you can turn anything into null, there is a difference between "I want a value, and if you fail to give it to me, I'll throw, because it is against my contract" and "I don't care if you give me this value". optional means "I don't care" on an input parameter, and explicitly tells the caller that they should expect "nothing" on return sometimes. (again, C#/Java forces that possibility on almost every interface, which is a pain for programmers in that language, but hey).
Admittedly, I wish there was a clean way to solve that. I just don't think Maybe/Optional does.

Code Contracts takes a stab at the underlying problem (programmatic verification), but from a code overhead standpoint it's abhorrent. It's a good tool, just ugly as hell.

korona
Posts: 495
Joined: Sun Jul 04, 2010 8:40 pm UTC

Re: Poll! What is the value of this constant?

Postby korona » Thu Sep 18, 2014 7:46 pm UTC

Yakk wrote:
Note that I'm only talking about imperative languages with relatively poor support for algebraic types, like Java or C++!

Hey, speak for Java. optional<T> works perfectly fine in C++ with little syntactic overhead at point of use.

Yes, optional<T> works fine in C++ and I'm happy that it made it's way into the standard library because it gives you optional value types. There is not much use of an optional pointer type in C++ as you could just use NULL/nullptr for that purpose.

But C++ has poor support for general algebraic data types. Defining a list through the equivalent of
List<T> = Nil | Cons<T, List<T>>
and then using pattern matching to define functions operating on lists is not very beautiful in C++. It can be done with boost::variant but it is quite ugly.


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 29 guests