cdsmith / HMock

Mock framework for testing in Haskell
BSD 3-Clause "New" or "Revised" License
23 stars 4 forks source link

Mocking polymorphic return values (general case) #3

Closed cdsmith closed 3 years ago

cdsmith commented 3 years ago

Migrated from TODO file.

class MonadFoo m where
    foo :: forall a. a -> m a

makeMockable ''MonadFoo

foo is unmockable. If you try to write a polymorphic expectation, like this:

-- Matches foo applied to any type of argument.
expectAny $ Foo_ anything |=> \(Foo x) -> return x

(|=>) has an ambiguous type, because we don't know the return value without also knowing the parameters.

My instinct is to try to promote the ambiguous type to a rank 2 type, thereby limiting what the programmer may write to those things which can unify with any type acceptable to foo. In order to represent the relationship between types of the arguments and return value, the type of Matcher would need to mention its argument types, as well. This could be done in a type-level list.

If we do this right and get lucky, it could generalize both the type-specific Rule with Typeable constraints and the existing behavior for polymorphic arguments, which are already universally quantified with rank-n types in the Matcher. But my attempts to make this work have so far failed.

cdsmith commented 3 years ago

Okay, now I understand why this doesn't work. To do this, I'd need to require Typeable constraints on all method arguments and return values. That is strictly worse than the current situation, where:

  1. Typeable constraints on arguments are only required if you want to match specific argument types.
  2. I can still make methods with Typeable return values mockable.