7

I'm writing a module that runs a finite state machine* based on the contents of an array of records passed in at initialization. Each record describes a state and includes instructions on how to act on inputs (i.e., when in state S1, input I triggers a transition to state S2). For the FSM to work correctly, the transitioned-to states need to exist.

My quandary is where to validate those transitions.

The defensive programmer in me says to do it once as early as possible, such as when the FSM is initialized. This means I can raise an error when I'm first handed bogus data and avoid handing back a process that may fail later as a result. The rest of the implementation won't have to be as defensive because the table is known to be good and any errors that Erlang decides to raise will be a result of implementation rather than having being fed bad data. It will also make debugging easier for those using the module since they'll get a badarg or something else back immediately instead of having to paw through my sources later to figure out that it was their mistake and not mine.

The Erlang philosophy seems to be that things should be left to run as long as possible, failing only when they have to and letting a supervisor take care of picking up the pieces. On one hand, this makes sense because a given FSM could run forever without encountering the inputs it would take for a bad transition. On the other, failing late puts the onus on the callers to write repetitive tests for something that's easy for me implement once.

I know that most rules in this business aren't hard and fast, but is one approach more "Erlangy" than the other? Would a fail-early implementation appear out of place if released for others to use?


*I am aware of the gen_fsm behaviour and that what I'm doing has some comparative shortcomings. This is a learning exercise for some other things, and a FSM happens to be something that incorporates them.

  • If I understand correctly, you're wondering if you can trust user input? – Florian Margaine Aug 22 '13 at 17:38
  • @FlorianMargaine: I never trust user input. The question is more about whether to pitch a fit about bogus input earler or later. – Blrfl Aug 23 '13 at 0:58
  • "The Erlang philosophy seems to be that things should be left to run as long as possible" - isn't the Erlang philosophy to 'fail-fast', which seems like the opposite of leaving things to run as long as possible? – Sean Burton Oct 4 '17 at 14:13
  • @SeanBurton The way I've always looked at fail fast is that it means not trying anything heroic in the face of a failure when it happens, just die. There's a subtle-but-important difference between that and fail early. – Blrfl Oct 4 '17 at 14:42
  • @Blrfl: Can you explain the difference between "die" and "fail early"? The former seems just a special case of the latter to me. – Giorgio Jan 2 '18 at 11:10
1

The reason it seems less "Erlangy" to do onerous type checking is because the language is dynamically typed.

If you really need to ensure that data of a certain type is used, you can use the is_TYPE/1 functions to verify your input as guard expressions. You should do this as early as you are able.

http://learnyousomeerlang.com/types-or-lack-thereof

  • 3
    This wasn't so much about type checking as verifying that the data passed in was self-consistent enough that the state machine would be able to run without croaking. But thanks for the answer; I'd thought this one was going to get dusty. – Blrfl Jul 23 '13 at 15:55
0

Joe's dictum. Fail fast, fail noisily, fail politely.

Yes check your data. Check every statement you can. So pattern match every call you can. If expecting an ok response from foo, write:

 ok = foo()

rather than:

foo()

Use guards to typecheck when you can.

In reality, it is possible to write poor code in any language.

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