How do strongly typed languages that have type inference, handle the unused returned values (where it's not obvious what the intended type is)?
Here's an example where this happens in the untyped Python, where it's of course not caught:
list(map(lambda item: item.some_method, some_collection))
Here item.some_method
was intended to be called, for its side effects, so the correct line is:
list(map(lambda item: item.some_method(), some_collection))
Without additional constraints, a strongly-typed language with type inference will typically not prevent the first example, as it is technically valid, as you point out. There are a few ways this could be avoided.
map
and for_each
, where the latter is expected to be impure/effectful. If you use for_each
in the first example, this would fail to compile, as the type of lambda item: item.some_method
is A -> () -> ()
, rather than A -> ()
. However, nothing prevents you from unidiomatically using map
here.map
would be expected to take a lambda that is pure (making lambda item: item.some_method()
invalid) and for_each
would be expected to take a lambda that is impure (making lambda item: item.some_method
invalid). This is enforced using monads, which act as a marker that tell the type system a value is impure (e.g. the type IO(A)
represents an effectful A
type).map
is annotated #[must_use]
in this way, then its return value must be used, which would alert you to an incorrect use of lambda item: item.some_method
(as the return value of map
would not be used). In Rust, the unit type ()
is automatically "used", and in theory this could be extended to include [()]
, in which case the correct code lambda item: item.some_method()
would work without issues.