
C# and F# approaches to illegal states - vkhorikov
http://enterprisecraftsmanship.com/2015/09/28/c-and-f-approaches-to-illegal-states/
======
thoth
I think folks are lost in the weeds of criticizing the specifics of the
example code and not paying attention to the larger design message - that
being F# provides language constructs that make it easier to avoid allowing
illegal states, and the type system will help detect that during compilation.

Yes it is a contrived example, yes you can write "better" C#, but this is a
short example for illustrative purposes.

I bet if somebody wrote a blog post about Djikstra's letter "GOTO considered
harmful" 45+ years ago, attempting to illustrate it with more examples, it
would have collected similar comments: nothing wrong with GOTO, I can use it
correctly; that example of spaghetti GOTO code is poor and not idiomatic; the
example code is too simple and nobody would write it that way; yes it takes
discipline to use GOTO without ill effects but nobody would
accidentally/maliciously subvert my GOTO hierarchy; etc. Meanwhile, the
original point about structuring code using different techniques to help avoid
problems (and avoid GOTO) would be lost.

It's like people are overly defensive about language features (or lack
thereof) as if writing extra boilerplate and not letting the compiler help is
a badge of honor.

EDIT: clarity

~~~
thomasz
I think that is highly questionable. Sum Types are a different approach with
some advantages and some disadvantages over inheritance. Nothing more and
nothing less. They really shine if you have clearly defined types that seldom
change. On the other hand, discriminated unions make it absurdly complicated
to defend against some errors that are almost comically easy to avoid by using
a standard OO approach:

    
    
      class Range {
          public int From {get;}
          public int To {get;}
          public Range(int from, int to) {
              Assert(from <= to);
              From = from; To = to;
          }
      }

~~~
thoth
Nobody says you have to use sum types to solve all problems in the design
domain - and as kvb points out, F# does have OO functionality too.

Your proposed problem might be better solved with the Option type and a simple
guard to return None if out of range.

>comically easy to avoid using a standard OO approach

This is an ironic statement given that your code, as written, is circumvented
by subclassing Range and side-stepping its constructor. I suppose you actually
meant "sealed class"... but we're getting off in the weeds again, missing the
fundamental point of the post.

~~~
thomasz
> This is an ironic statement given that your code, as written, is
> circumvented by subclassing Range and side-stepping its constructor.

You are the second one who says this. I must admit that I do not understand
how this would be possible. The properties are not virtual, and not writable
for anything but the base constructor. To my knowledge, it's impossible avoid
the base classes constructor by subclassing. I'm pretty sure you can actually
circumvent the assert, but you would have to use the heavy weapons in the
reflection API.

    
    
        class Range
        {
            public int From { get; }
            public int To { get; }
            public Range(int from, int to)
            {
                Assert(from <= to);
                From = from; To = to;
            }
        }
    
        class LolRange : Range
        {
            public LolRange() : base(0,1)
            {
                From = 1; To = 0;
            }
        }
    
        Error CS0200 Property or indexer 'Program.Range.From' cannot be assigned to -- it is read only
        Error CS0200 Property or indexer 'Program.Range.To' cannot be assigned to -- it is read only

------
thomasz
I don't get it. It is perfectly valid to use immutability and the Type system
to enforce those invariants in c#.

    
    
        abstract class User {
            public string Name { get; }
            abstract bool IsActive { get; }
            
            public User(string name) {
                this.Name = name;
            }
        }
    
        class ActiveUser : User {
            public ActiveUser(string name) : base(name) { }
            private ICollection<Subscription> _subscriptions = new List<Subscription>()
            public IEnumerable<Subscription> Subscriptions => _subscriptions;        
    
            IsActive => true;
            public DeletedUser Delete() => new DeletedUser(Name, DateTime.Now);
            public void AddSubscription(Subscription s) => Subscriptions.Add(subscription);
    
        }
    
        class DeletedUser : User {
        
            public DeletedUser(string name) : base(name) { }
            public DateTime DeletionDate {get;}
            IsActive => false;
        
            public DeletedUser(string name, DateTime deletionDate) : base(name) {
                this.DeletionDate = deletionDate;
            }
    
            public ActiveUser Reactivate() => new ActiveUser(Name);
        }

~~~
kvb
But someone else could create a subclass breaking the invariants. It's
possible to avoid this by making the abstract class's constructor private and
nesting the two subclasses inside of the abstract class, but this is not
idiomatic. If you have to work that hard then most people won't bother, while
in F# it's very natural to create such a design.

~~~
seanmcdirmid
It is easy to do in C# and I do it often (seal your concrete subclasses if you
are paranoid). F# does provide better syntax for it though...I wouldn't be
surprised if under the covers they are using classes. C# still doesn't provide
pattern matching to make it very useful...but someday I hope that happens.

~~~
saosebastiao
Yes, it does use classes under the covers. I've never consistently used an OO
language before, but I've been using C# lately, and knowing how F# can be
built on top of a VM that was built for C# has definitely helped me to bring
some of these concepts with me. I'm probably not writing idiomatic C#, but I
think I'm okay with that.

------
jameshart
And now you've baked your understanding of reality into your type system or
your invariant contracts, you come to write some code that actually works with
this API and realize you've made a terrible mistake, because when you delete a
user, you need to know which subscriptions to cancel, but you've already
thrown that information away as soon as you called 'Delete()'. Maybe we should
restrict you from deleting a user while they have active subscriptions? Well,
that's easily accomplished with a C# invariant, a little harder to bake into
the F# Type union.

In general, though, it turns out you often need to be able to have objects in
an 'illegal' state some of the time. "Zip code is a mandatory field" \- but
that doesn't mean you can't save your order form for later if you need to go
and look up the zip code. It's generally best to validate objects as being
'eligible' for a particular operation, not to prevent them from being
ineligible altogether - An object with no zipcode _can_ be saved, but it _can
't_ be submitted. Structuring that sort of thing into a type system - while
doable - is probably not idiomatic in either F# or C#.

~~~
ionforce
If you're experiencing some sort of mismatch with your model, it means that
your model is incorrect.

~~~
jameshart
But if your model is churning every time your understanding of the business
rules changes, then you have a different problem.

~~~
noblethrasher
Your model will usually be incorrect because in all but the most trivial
applications the model will be the result of experimentation. Even if you
“accurately”[1] model the business rules, those rules themselves are subject
to change as the business revives it’s model of the market, or customer
relationship, or regulatory environment, or whatever.

The main selling point of extreme late-binding (e.g. ad-hoc polymorphism,
dependency injections, message passing, etc) is that lets you quickly revise
your model.

[1] Whatever that means.

------
alkonaut
The F# code is no doubt more elegant because of discriminated unions and
pattern matching, but the C# code in this comparison is a bit lazy/sloppy if
you ask me.

I know it's a made up example to showcase discriminated unions, but it becomes
a bit of a strawman to suggest that C# doesn't even allow representing
discriminated unions. It does: through inheritance and tons of boilerplate
code.

The correct demo I think would have been to make the _correct_ type (which
doesn't allow invalid states to be represented) in both languages, to show how
in C# it required maybe hundreds of lines, and in F# it's just a few lines.

Under the hood, almost exactly the same types are created from F#, where the
F# types when seen in C# or IL of course look very much like the types you had
to meticulously type in C#.

~~~
DanielBMarkham
But no.

The point was to show how when you're thinking in a language that supports
algebraic types and pattern-matching, _you automatically exclude states that
can 't exist_.

It's not that the underlying CLR or IL is any different given the same
solution -- that's just measuring a language's verbosity. It's that some
language constructs automatically lead you down the path to correct code
without having to boil the ocean.

Sidebar: I would have tightened the F# code up a bit. No need to have the Name
string repeated in both types.

~~~
inglor
Or, you could use a class Hirarchy with a User abstract class and ActiveUser
and DeletedUser subclasses and use the type system to enforce the constraints.

Using a nullable field and an enum is hardly the standard way in C#.

~~~
nbevans
But unfortunately it is the one a lot of C#'ers reach for because it is the
smallest amount of code in that language and often results in the least
refactoring effort.

A properly designed model in C# like suggested is a large number of lines of
code. And then because there is no pattern matching, you have to hand-roll
your type checks/casts and make sure have that method throw an unconditional
exception at the end for literally impossible states. Then if you make a
change to this model, i.e. you add a new "SuspendedUser" subclass, you have to
make sure you go through the codebase to update all those places you wrote a
hand-rolled "pattern match" against that type. Because the compiler won't warn
you about an unrepresented state on your type checks. And so then you have to
make sure the whole project team is aware and you have to make sure new hires
are familiar with the patterns being used. So yeah, the problems just snowball
and then you wonder why you didn't just use a more appropriate language in the
first place that has already taken complete care of all these trivial
programming issues.

------
mariusmg
"The technique C# traditionally uses to deal with illegal states is called
design by contract"

That's not really true. Code contracts are just a library , it's certainly not
"traditional" in C# to use that to check for invalid state. Traditionally you
just check the value before assignment. And if you do this your entire problem
"goes away".

Gotta say this is one of the most contrived "F# is better than C#" example i
ever saw.

~~~
thomasahle
> Traditionally you just check the value before assignment. And if you do this
> your entire problem "goes away".

I wouldn't say spreading null checks and forgetting them all over your code
makes any problems "go away".

~~~
mariusmg
Yes, you're right. It's better to blindly do assignments and use code
contracts to check the state after. Much cleaner. </sarcasm>

~~~
Retra
Ever hear the term "false dichotomy?"

------
bad_user
While the solution described is cool, it only applies to problems that can be
solved with union types in F#.

That's cool, but things get much more complicated when trying to describe the
concept of a non-empty list (still possible, but I don't believe is included
in F#'s library), or near impossible when trying to describe things like a
number within a certain range, or various relationships between the arguments
of a function or of a type.

I mean, when you're constructing something like "Segment(x, y)" (or "Segment
of double * double" in F#, not sure if I got the syntax right), how do you
express that the X coordinate needs to be lower than the Y coordinate? And
then the biggest modern challenge is to model state machines for asynchronous
communications, which implies working in the presence of non-determinism. Good
luck modeling that with a static type system.

Basically there are things that can be expressed in a statically typed way, by
means of a good enough type-system, but there are always invariants that can
only be checked at runtime. Hence assertions sprinkled in the code are still
valuable even in a language like F#, because if you're going to fail, it's
better to fail as fast as possible.

~~~
wereHamster
Can't comment on F#, but in Haskell you'd create a special datatype and use
smart constructor which only allows to create a value which conforms the
requirements:

    
    
        data Segment = Segment Double Double
    
        mkSegment :: Double -> Double -> Maybe Segment
        mkSegment x y
          | x < y     = Just (Segment x y)
          | otherwise = Nothing
    

There are things you can check at compile time, and for that F# and other
strongly typed languages are great. And if that's impossible they can still
_force_ you to do these checks at runtime.

And with such great support for custom types, you can rely on complex
invariants in large parts of your application. In the example above, if you
have a `Segment`, you _know_ that x < y, even if the compiler can't prove it.

~~~
bad_user
So what stops anybody from constructing a Segment manually, bypassing
mkSegment?

Assuming that you can make Segment's constructor private, that's indeed a good
pattern sometimes, but leaving mkSegment as the only way to build a segment
will make working with segments pretty inconvenient. Imagine that what you
usually want to do with segments are intersections and unions and if the
original segments already have this x < y invariant, then the algorithms, if
correct, will also have it. Therefore forcing usage of mkSegment will be a
pain in the ass, because then you have to deal with the Maybe monad
everywhere.

As @pron is saying, this is still a runtime check. And the problem is, there
is no need for it unless you receive outside input that needs to be processed.
Otherwise the internal algorithms should always respect the contract anyway.
Consider this ...

    
    
        intersect :: Segment -> Segment -> Maybe Segment
    

What would a Nothing result mean? Would it mean that the 2 segments do not
intersect, or would it mean that one of the segments was invalid? Treating the
error in this signature is totally unneeded and forcing usage of mkSegment
would do exactly that.

In Scala I sometimes use the mkSegment pattern that you described when
receiving data from the outside (like from the database), but in this
particular instance I prefer doing the following, because of the above
reasoning (i.e. Segments should already be valid, input should be validated
elsewhere, if we've got invalid segments then we need to fail as fast as
possible):

    
    
         case class Segment(x: Double, y: Double) {
           require(x <= y)
         }
    

And I find this to be totally fine, in addition to whatever the static type-
system can pull off.

~~~
the_af

        intersect :: Segment -> Segment -> Maybe Segment
    

> _What would a Nothing result mean? Would it mean that the 2 segments do not
> intersect, or would it mean that one of the segments was invalid?_

The way I see it, it can only mean that they do not intersect, and _never_
that one of the segments was invalid. This is because a Segment is guaranteed
to be valid by construction; after all, that's precisely the property you
bought by using _mkSegment_!

~~~
bad_user
Yes but by using _mkSegment_ in the implementation of _intersect_ , you have
to treat its _Nothing_ result, even though it can never happen and so you
either end up with this reflected in the signature of _intersect_ itself, or
you cheat in the implementation of _intersect_ by assuming that _Nothing_ will
never happen, which would defeat the purpose of _mkSegment_.

~~~
the_af
It's a bit cumbersome, yes, but I don't think it defeats the purpose of
_mkSegment_.

Let's see: because _mkSegment_ is the only way to construct a _Segment_ , you
know the arguments to _intersect_ are valid. So there cannot be any doubt
about the meaning of _Maybe_ in the result; it simply cannot refer to invalid
arguments. There is only one other possible meaning: that the segments don't
intersect, which is the idiomatic use of _Maybe_ (another reason why I
wouldn't use _Maybe_ in this case to signal an invalid argument: "which
argument?").

Yes, this means that even if there is an intersection, _mkSegment_ forces you
to handle the _None_ case, even though you know by construction this will
never happen. It's annoying but nothing too terrible. I don't see how this is
cheating; you are simply using a property that the type system is unable to
catch... but this property _exists_ nonetheless.

------
deanCommie
It would require some type checking and casting (or runtime exceptions) but
wouldn't the C# equivalent to the F# approach be abstract class User, class
ActiveUser : User, and class DeletedUser : User

~~~
drabiega
I believe this is what the author is referring to at the end:

>Unfortunately, the lack of discriminated unions and pattern matching makes
the F#’s approach mostly unbearable to implement in C#. It’s possible in some
simple cases but still remains pretty tedious.

~~~
inglor
Good point. No, I don't believe that's what they mean there. The OP defines
the F# version user as a union of either an activeuser or a deleteduser.

The C# idiomatic way would be to use an abstract class and implement two
subclasses.

The difference is that in the C# way someone could in theory (if they're your
evil wizard arch nemesis for example) create a third user subclass and harm
the type soundness. In practice you can guard against that as well if you
really have to guard against evil wizards.

------
fsloth
I don't think there's anything stopping from implementing ActiveUser and
DeletedUser as separate classes in C# as well? It's much more succinct in F#
but so is pretty much everything else.

------
huhtenberg
> _Design by contract_

Back in the day we used to call it an _assert_ , sprinkled it over code to
cover the invariants and generally didn't make much fuss about it. Kids these
days... "design by contract"... such words, much wow :-P

~~~
rbehrends
Design by Contract isn't just asserts. The only thing that contracts and
asserts have in common is that they are expressed as semantic predicates.

DbC formalizes the idea of a contract (hence the name) between the caller and
callee (in Eiffel parlance, the client and supplier of a service) and what
each expects of the other and is part of the interface of a class.

Accordingly, there are rules for contracts (visibility of symbols used in a
contract must match the visibility of the function it is attached to, how
contracts are related to each other via inheritance, etc.).

It is not a terribly complicated concept, but it's also not just asserts.

~~~
gnaritas
> Design by Contract isn't just asserts.

That's exactly what it is, but it's a very specific style of asserts with very
specific rules as to what and when to assert; it is however, still just a
bunch of asserts.

~~~
rbehrends
Okay, then explain to me how (say) in C++ you can annotate pure virtual
methods with contracts or how inheritance of contracts can be done in C++,
assuming you want to implement them using asserts. For that matter, how do you
implement class invariants, which need to be checked before and after each
qualified call to an instance of the class, but not when the call is
unqualified?

The other essential part that asserts don't really capture is that contracts
(as pre- and postconditions) are part of the API, while asserts are part of
the implementation.

~~~
gnaritas
> in C++

You can't blame me because some language you choose has limited meta abilities
and doesn't allow you to place asserts anywhere. An assert is a generic
concept that you can do in any language, and in languages with meta-facalities
all of that is trivial to do, you're too focused on "where" the assert is in
your specific language, but DBC came from Eiffel, not C++, and checking
conditions or blowing up is an assert no matter where it occurs. DBC is cool,
the places DBC chooses to do it's asserts are also a big part of what DBC is,
but it's still all just some asserts in creative places under creative
inheritance rules.

~~~
rbehrends
Well, if you are sufficiently fuzzy when it comes to equivalence, then every
programming language is just about shuffling bits around registers and they're
all one and the same thing.

But in the end, interface (contracts) and implementation (asserts) aren't
actually the same thing. Having commonalities is not the same thing as being
the same.

~~~
gnaritas
Well I'm not going to argue about it, but I agree with OP, they're just
asserts used in an idiomatic way.

------
nowprovision
Granted the F# approach is somewhat nicer, but I don't think the C# approach
with contracts is too bad (no recent experience with .Net - 6yrs ago contracts
were painful to use), but you also could go the immutable route and via a
method call easily return a new instance of User and enforce the invariant
prior to creating a new instance etc.. It's a minor issue in the end, the
bigger issue is that your client/stakeholder knows what they actually want and
you actually capture those in-variants exhaustively and correctly
(implementation is minor, F#/ocaml/haskell can't save you here)...

~~~
romaniv
AFAIK, code contracts in .NET still have all the issues they had several years
ago. The absolute worst one being that you need to install something for them
to actually work. This means different computers will have different code
behavior.

------
verinus
In my opinion and from the comments I think that encoding one state is not a
problem either in C# or F#. Now assume we have to add an additional state to a
user and all proposed subclassing solutions in C# turn out to be what they
are: not optimal.

My biggest problem with OO is, that it only allows us to structure code in a
one dimensional way (inheritance) but in reality we will face more complex
scenarios. and even this way is increasingly discouraged: composition over
inheritance...

~~~
achr2
C# certainly has other options to straight inheritance like explicit
interfaces and extension methods.

------
brudgers
_We can depict this solution in the following way:_

    
    
      [table labeled :Users:]
    

In my opinion that's a better way of encoding the desired behavior than either
the C# or F# code by several orders of magnitude. It can be read to find out
what privileges a user state allows. It can be read to find out what user
states is required to access a privilege.

The key is that tables are readable. By anyone. Even non-programmers. All the
behavior is right there. The question about what's the best way to represent
it in code is not F# versus C#. It's automatically versus wrong. The table
should be the point of truth. [1]

[1]: The point of departure is Aaron Brooks' idea of live specification. I
don't claim to accurately represent his view. Listen for yourself here:
[http://blog.cognitect.com/cognicast/074](http://blog.cognitect.com/cognicast/074)

------
rwmj
Be good to also compare this to SQL databases like PostgreSQL, where you can
have quite complex check constraints, non-nullable columns and unique indexes.
If done properly it can make it literally impossible to insert bad data into
your database.

------
sklivvz1971
It's a contrived example. There is no need to store the "state" of a user if
you can infer it from other state. Just define a "deleted" user as a user with
a deletion date...

Modeling is hard, maybe it should be done more accurately...

~~~
alkonaut
And even if it was required (Say there were 3 states) it's an obvious smell to
have a field in a type that is used only in one "state" of the object. The
presence of such a field is the most obvious clue that one needs a new class
for that state.

~~~
jameshart
That's far from a universal truth.

Only US users have a 'social security number' field. Should US users be a
different class than German users?

Only cars have a 'passenger count' field. Only electric vehicles have a
'battery capacity' field. What type system can accommodate both Teslas and
Harley Davidson LiveWire bikes?

~~~
alkonaut
Social security, maybe a field can be of a "national identifier id" number? I
have an id number but parsing and validating is different from the us version.
Since it's a different format and different logic I don't see why another type
wouldn't be useful.

Passenger number: "0" seems like a valid number that can make sense even for
non-passenger cars.

I agree it's not black and white, though.

------
mannykannot
This is all very well for the single binary state of the example, but as the
number of states increases, do you not get a combinatorial explosion in the
number of types and state-transition functions?

~~~
eropple
This is my concern with F# for sure (and I like F#, though I haven't used it
in anger sufficiently). When using Scala, I had to minimize the times I did
this sort of thing, usually across a single axis rather than the multiple ones
I'd want to do it across.

Which doesn't mean I dislike the whole concept, but it's way more limited than
this article wants you to think.

------
wehadfun
What would be the approach if user had another state such as RestrictionLevel
{admin, normal}. Would the we make a subsclass for every combination between
actibe and RestrictionLevel?

------
agumonkey
Isn't it related to phantom types ?

