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 out-of-scope types (use -fprint-potential-instances 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:
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
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
asserts 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
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
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.
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
from typing import Callable def hof(f: Callable[[ndarray, ndarray, ndarray, ndarray, ndarray], bool]): ...