Skip to content

Python-side conversion of string doesn't preserve equality #717

Closed

Description

@oxinabox

with

import juliacall
import Main from juliacall as jl

The following all return false.
I expected them all to return True

jl.String("a") == juliacall.convert(jl.String, "a")
"a" == juliacall.convert(jl.String, "a")
juliacall.convert(jl.String, "a") == jl.String("a")

The following do return true:

jl.String("a") == jl.String("a")
juliacall.convert(jl.String, "a") == juliacall.convert(jl.String, "a")
"a" == jl.String("a")

I think what is going wrong is that juliacall.convert wraps the string in an AnyValue even though it doesn't have to.
Where is jl.String actually doesn't:

In : type(juliacall.convert(jl.String, "a"))
Out: juliacall.AnyValue

In : type(jl.String("a"))
Out: str

And AnyValue does not overload equality.
In contrast other similar types return wrapper types that do.

In : type(juliacall.convert(jl.Float64, 1.5))
Out: juliacall.RealValue

In : juliacall.convert(jl.Float64, 1.5) == 1.5
Out: True

In : type(juliacall.convert(jl.Bool, True))
Out: juliacall.IntegerValue

In : juliacall.convert(jl.Bool, True) == True
Out: True

Activity

  1. cjdoris commented on Nov 12, 2025

    @cjdoris
    Member

    Yes this behaviour is as designed but isn't very ergonomic as you've spotted.

    • jl.String(...) returns a Python str because calling a Julia value automatically converts the result to a Python value.
    • juliacall.convert(...) returns a Julia wrapper AnyValue (or a.subtype) because you are asking for something with a particular Julia type so it only makes sense to return a Julia value.

    In JuliaCall v1, the first point above will change - most operations on wrapped Julia values (such as function calling) will return wrapped Julia values. This mirrors how Py works and is much more predictable - in particular it's much easier to tell what will be a plain Python value and what will be a Julia value.

    You can try out the v1 branch to get a preview of this.

    Closing as this is intentional behaviour.

  2. oxinabox commented on Nov 13, 2025

    @oxinabox
    ContributorAuthor

    Yes, I think this will be much clearer.
    Explict julia objects that I can convert in post-processing to python objects will be less confusing.
    E.g. i realised that it is very hard to construct a Pair to pass to a julia function that explictly takes a Pair (not a tuple).
    Since jl.Pair(x,y) gets auto converted into a tuple

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions