Open
Description
I want to be able to write functions with precise RDF typing, that e.g. accept a number literal but not other types of Literal
. Currently Literal
is just a single class that isn't generic, so there's no way to enforce this.
One way I could see this working is by making a subclass corresponding to each XSD type:
class IntegerLiteral(Literal):
datatype = XSD.integer
def __init__(self, value: int): ...
And then you could it work with the base Literal
class using something like:
from typing import overload, Literal as TypingLiteral
class Literal(Identifier):
@overload
def __new__(
cls,
lexical_or_value: int,
datatype: TypingLiteral[XSD.integer] = XSD.integer,
) -> IntegerLiteral:
...
@overload
def __new__(
cls,
lexical_or_value: bool,
datatype: TypingLiteral[XSD.boolean] = XSD.boolean,
) -> BooleanLiteral:
...
def __new__(
cls,
lexical_or_value: Any,
lang: str | None = None,
datatype: str | None = None,
normalize: bool | None = None,
) -> Literal:
Alternatively, you could make Literal
generic:
from typing import overload, Literal as TypingLiteral
class Literal[T](Identifier):
@overload
def __new__(
cls,
lexical_or_value: int,
datatype: TypingLiteral[XSD.integer] = XSD.integer,
) -> Literal[int]:
...
@overload
def __new__(
cls,
lexical_or_value: bool,
datatype: TypingLiteral[XSD.boolean] = XSD.boolean,
) -> Literal[bool]:
...
def __new__(
cls,
lexical_or_value: Any,
lang: str | None = None,
datatype: str | None = None,
normalize: bool | None = None,
) -> Literal:
Then my downstream code could do something like:
def some_function(
arg: IntegerLiteral | URIRef
):
...
Activity
ashleysommer commentedon Apr 2, 2025
@multimeric This is one of those things that seems logical and easy to implement on the surface, but is flawed in several ways that makes it very difficult or impossible to actually implement.
The main reason is that typings for RDF Literals does not map well to the Python typing system.
Eg, what happens if you parse a lexical that looks like this:
"true"^^xsd:integer
This is a valid RDF Literal, RDFLib will honor its explicit parsed datatype, but its value will be a String
"true"
and it is marked asill-typed
internally. Would the generic type of this be a Python bool, or a python Integer, or a python string?Or similarly, you do this:
Again, this is an ill-typed Literal, but its still a valid RDF literal. Would the generic type of this be a Python float or a python Integer?
Even without ill-typing:
All floating-point numbers in Python are "double-precision", its not possible to pass in a value to the constructor that matches the specified datatype.
We could implement some simple fool-proof cases, but it would probably never work how you want it to, and not all RDF typing issues would be caught by static type checkers.
multimeric commentedon Apr 2, 2025
I think this is fine. My proposed implementation only affects the type system and not at runtime, so if you do
Literal(1.23, datatype=XSD.integer)
it will create a float literal with datatype integer as it does now. The only difference is that the type checker will complain. This is exactly what I want because it doesn't break anything but it does allow better static analysis.