Skip to content

Instantly share code, notes, and snippets.

@stroxler
Last active December 16, 2021 22:33
Show Gist options
  • Save stroxler/01fafd28cede73a351c51a61e76703b0 to your computer and use it in GitHub Desktop.
Save stroxler/01fafd28cede73a351c51a61e76703b0 to your computer and use it in GitHub Desktop.
Examples of callable type vs union confusion
  1. We can't naively union a callable type with another type:
union_of_callables: (int) -> str | (bool) -> str  # Syntax error!

# Needs extra parens; Not intuitive or user-friendly.
union_of_callable2: ((int) -> str) | ((bool) -> str)

# Simpler and can be easily split into multiple lines, as Никита mentioned.
union_of_callables3: Union[(int) -> str, (bool) -> str, (list[int]) -> str]

Likewise, int | (str) -> bool is a syntax error.

  1. Another huge gotcha is with Optional callables if we naively use <new-syntax> | None:
old: Optional[Callable[[int], str]]

# This works fine
with_pipe: Callable[[int], str] | None

# Incorrect! This specifies a non-optional function with return type `str | None`
naive_translation_to_new_syntax: (int) -> str | None   

# Trying to use order of types to fix a precedence issue won't work
naive_attempt_to_fix_above_error: None | (int) -> str  # Syntax error!

# The solution requires parentheses if we're using `<type> | None`
intended: ((int) -> str) | None

# Expressing it with Optional (If we don't deprecate optional!) is clean:
unambiguous_and_simple: Optional[(int) -> str]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment