Expand the two type synonyms to see what survives an exception:
In the first order, a failed computation can still produce (Nothing,s'). The exception handler therefore runs with , including writes made before failure. In the second order, failure produces Nothing, with no returned computer memory. A handler defined by applying the underlying orelse pointwise runs its alternative from the original input computer memory . This gives rollback of this explicit program state for the failed branch.
For example, start with a memory location holding , write , fail, and handle the failure by reading that memory location. The first order returns ; the second returns . This rollback concerns the explicit computer memory only: arbitrary effects in the base computational monad are not thereby reversed.
A conventional programming language normally retains writes made before an exception is caught. Its mutable computer memory and exception handling therefore correspond more closely to .
A word can be a string, and the input text a list of those strings. Use names that avoid clashing with the Standard Prelude machine-word type Word:
type TextWord = String
type Text = [TextWord]
Thus . These are type synonyms, so ordinary string and list operations remain available without wrapping or unwrapping constructors. They describe input that has already been tokenized; punctuation and word boundaries do not need a separate treatment here.
A type synonym is a name for an existing type expression, introduced with type, for example type Name = String. It improves documentation and makes type signatures easier to maintain. It creates no new distinct type: a synonym and its expansion remain interchangeable. An algebraic data type declared with data or newtype has a different role.
Use a type synonym for names and an algebraic data type with the two teams and their goals in the given positional order:
type Team = String
data Match = Match Team Int Team Int
Team is a synonym, not a new runtime wrapper. Match on the left is the type constructor, while the same spelling on the right is a data constructor in a different namespace. Its value-level type signature is Match :: Team -> Int -> Team -> Int -> Match.
Use coordinates with row and column in :
type Place  = (Int,Int)
type Places = [Place]
type Board  = (Places,Places,Places)
data Player = O | X deriving (Eq,Ord,Show)
The components of Board record the positions occupied by O, occupied by X, and still empty, respectively. On a nonterminal legal board, equal occupied counts mean O moves next; one extra O means X moves next.
The type synonym does not enforce the representation invariant: coordinates are in range; each component has no duplicates; the three represented sets form a set partition of the nine positions; and the occupied counts differ as just described. Both players cannot have won. A winning O board has one extra O, and a winning X board has equal counts. More fundamentally, a legal tic-tac-toe state has a legal alternating history that stops at the first win: satisfying a type alone never licenses continuing an already completed game.
Use arbitrary-precision integers for positions, avoiding overflow on infinite sequences:
type Seq a = a -> [Integer]

rep :: Eq a => [a] -> Seq a
rep xs v = [i | (i,x) <- zip [0..] xs, x == v]
The type synonym Seq a needs no equality constraint; the equality test is required specifically by rep. The list comprehension visits input positions in increasing order, so the output is an increasing list of precisely the matching positions. For finite inputs this includes a terminating empty list when the value is absent. Infinite inputs require the additional productivity distinctions discussed below. This is an occurrence-position representation of a sequence.