The World’s Largest Online Community for Developers
def f(x, y):
return x & 1 == 0 and y > 0
g = lambda x, y: x & 1 == 0 and y > 0
Now the same thing in Haskell:
import Data.Bits
f :: Int > Int > Bool
f x y = (.&.) x 1 == 0 && y > 0
That works, however this doesn't:
g = \x y > (.&.) x 1 == 0 && y > 0
Here's the error this gives:
someFunc :: IO ()
someFunc = putStrLn $ "f 5 7: " ++ ( show $ f 5 7 ) ++ "\tg 5 7: " ++ ( show $ g 5 7 )
• Ambiguous type variable ‘a0’ arising from the literal ‘1’
prevents the constraint ‘(Num a0)’ from being solved.
Relevant bindings include
x :: a0 (bound at src/Lib.hs:13:6)
g :: a0 > Integer > Bool (bound at src/Lib.hs:13:1)
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Num Integer  Defined in ‘GHC.Num’
instance Num Double  Defined in ‘GHC.Float’
instance Num Float  Defined in ‘GHC.Float’
...plus two others
...plus one instance involving outofscope types
(use fprintpotentialinstances to see them all)
• In the second argument of ‘(.&.)’, namely ‘1’
In the first argument of ‘(==)’, namely ‘(.&.) x 1’
In the first argument of ‘(&&)’, namely ‘(.&.) x 1 == 0’

13  g = \x y > (.&.) x 1 == 0 && y > 0
 ^
How do I get the same error in Python?  How do I get errors when the input doesn't match expectations?
To be specific, how do I say that a function/lambda MUST have:
__matmul__
(@
)bool
I know that I can roughly do this with: (docstrings and/or PEP484) with abc
; for classes. But what can I do for 'loose' functions in a module?
Generally you have three possible approaches:
1 + 'foo'
is a TypeError
).Your specific points:
 arity of 5
Define five parameters:
def f(a, b, c, d, e): ...
 each argument must be numerical
Either static type annotations:
def f(a: int, b: int, c: int, d: int, e: int): ...
And/or runtime checks:
def f(a, b, c, d, e):
assert all(isinstance(i, int) for i in (a, b, c, d, e))
def f(a, b, c, d, e):
if not all(isinstance(i, int) for i in (a, b, c, d, e)):
raise TypeError
assert
s are for debugging purposes and can be disabled, an explicit if..raise
cannot. Given the verboseness of this and the duck typing philosophy, these approaches are not very pythonic.
 each argument must implement
__matmul__
(@
)
The most practical way is probably to let the runtime raise an error intrinsically if the passed values do not support the operation, i.e. just do:
def f(a, b):
return a @ b # TypeError: unsupported operand type(s) for @: ... and ...
If you want static type checking for this, you can use a typing.Protocol
:
from typing import Protocol
class MatMullable(Protocol):
def __matmul__(self, other) > int:
pass
def f(a: MatMullable, ...): ...
In practice you probably want to combine this with your previous "each argument must be numerical" and type hint for a type that fulfils both these requirements.
 return
bool
def f(...) > bool: ...
Especially given that the @
operator is mostly used by 3rd party packages like numpy, in practice the most pythonic implementation of such a function is probably something along these lines:
import numpy as np
from numpy import ndarray
def f(a: ndarray, b: ndarray, c: ndarray, d: ndarray, e: ndarray) > bool:
return np.linalg.det(a @ b @ c @ d @ e) > 0 # or whatever
You can't directly translate the same typing expectations from Haskell to Python. Haskell is an insanely strongly typed language, while Python is almost the complete opposite.
To type hint a higher order function that accepts such a function as argument, use typing.Callable
:
from typing import Callable
def hof(f: Callable[[ndarray, ndarray, ndarray, ndarray, ndarray], bool]): ...