Roger-luo / Moshi.jl

nextgen MLStyle: Generic Algebraic Data Type + Pattern Match
http://rogerluo.dev/Moshi.jl/
MIT License
64 stars 2 forks source link

Problem with types #22

Open lehoff opened 1 month ago

lehoff commented 1 month ago

I have define the following:

module TVJulia

@data MinuteOutcome begin
    HomeGoal
    AwayGoal
    NoGoal
    BothGoal
end

@data Score begin
    ZeroZero
    ZeroOne
    OneZero
    OneOne
    Even
    Plus(Int)
    Minus(Int)
end

# const SCORES::Vector{Score.Type} = [
#     Score.ZeroZero, Score.ZeroOne, Score.OneZero, Score.OneOne, Score.Even,
#     Score.Plus(1), Score.Plus(2), Score.Plus(3), Score.Plus(4), Score.Plus(5),
#     Score.Minus(1), Score.Minus(2), Score.Minus(3), Score.Minus(4), Score.Minus(5)]
function scores() 
    Score.Type[
        Score.ZeroZero::Score.Type, Score.ZeroOne, Score.OneZero, Score.OneOne, Score.Even,
        Score.Plus(1), Score.Plus(2), Score.Plus(3), Score.Plus(4), Score.Plus(5),
        Score.Minus(1), Score.Minus(2), Score.Minus(3), Score.Minus(4), Score.Minus(5)
    ]
end

function Base.:+(s::Score.Type, mo::MinuteOutcome.Type)
    @match mo begin
        MinuteOutcome.HomeGoal => 
            @match s begin
                Score.ZeroZero => Score.OneZero
                Score.ZeroOne  => Score.OneOne
                Score.OneZero  => Score.Plus(2)
                Score.OneOne   => Score.Plus(1)
                Score.Even     => Score.Plus(1)
                Score.Plus(n)  => Score.Plus(min(n+1, 5))
                Score.Minus(1) => Score.Even
                Score.Minus(n) => Score.Minus(n-1)
            end
        MinuteOutcome.AwayGoal =>
            @match s begin
                Score.ZeroZero => Score.ZeroOne
                Score.ZeroOne  => Score.Minus(2)
                Score.OneZero  => Score.OneOne
                Score.OneOne   => Score.Minus(1)
                Score.Even     => Score.Minus(1)
                Score.Plus(1)  => Score.Even
                Score.Plus(n)  => Score.Plus(n-1)
                Score.Minus(n) => Score.Minus(min(n+1, 5))                
            end
        MinuteOutcome.BothGoal =>
            @match s begin
                Score.ZeroZero => Score.OneOne
                Score.ZeroOne  => Score.Minus(1)
                Score.OneZero  => Score.Plus(1)
                Score.OneOne   => Score.Even
                _ => s
            end
        MinuteOutcome.NoGoal => s
    end
end

But I am running into problems - one after the other.

> TVJulia.scores()
MethodError: Cannot `convert` an object of type Type{Main.TVJulia.Score.ZeroZero} to an object of type Main.TVJulia.Score.Type

Closest candidates are:
  convert(::Type{T}, ::T) where T
   @ Base Base.jl:84
  Main.TVJulia.Score.Type(::Any)
   @ Main.TVJulia none:0

Stacktrace:
 [1] setindex!(A::Vector{Main.TVJulia.Score.Type}, x::Type, i1::Int64)
   @ Base ./array.jl:1021
 [2] (::Base.var"#114#115"{Vector{Main.TVJulia.Score.Type}})(i::Int64, v::Type)
   @ Base ./array.jl:456
 [3] afoldl(::Base.var"#114#115"{Vector{Main.TVJulia.Score.Type}}, ::Int64, ::Type, ::Type, ::Type, ::Type, ::Type, ::Main.TVJulia.Score.Type, ::Main.TVJulia.Score.Type, ::Main.TVJulia.Score.Type, ::Main.TVJulia.Score.Type, ::Main.TVJulia.Score.Type, ::Main.TVJulia.Score.Type, ::Main.TVJulia.Score.Type, ::Main.TVJulia.Score.Type, ::Main.TVJulia.Score.Type, ::Main.TVJulia.Score.Type)
   @ Base ./operators.jl:544
 [4] getindex(::Type{Main.TVJulia.Score.Type}, ::Type, ::Type, ::Type, ::Vararg{Any})
   @ Base ./array.jl:455
 [5] scores()
   @ Main.TVJulia ~/git/lehoff/TVJulia/src/TVJulia.jl:97
 [6] top-level scope
   @ In[97]:1

When I try to use my + operator I get:

> TVJulia.Score.ZeroZero + TVJulia.MinuteOutcome.HomeGoal

MethodError: no method matching +(::Type{Main.TVJulia.Score.ZeroZero}, ::Type{Main.TVJulia.MinuteOutcome.HomeGoal})

Closest candidates are:
  +(::Any, ::Any, ::Any, ::Any...)
   @ Base operators.jl:587

Stacktrace:
 [1] top-level scope
   @ In[88]:1

This leaves me rather confused.

Happy to conduct further investigations if that would be of any help.

Roger-luo commented 1 month ago

Unfortunately we can't have real singleton (e.g type is the same as instance) this is not supported by Julia, so you need to explicitly write the instance (otherwise that's just a variant type). I was actually thinking this, maybe we should just remove the singleton syntax.

lehoff commented 1 month ago

Not sure I understand what I need to do - could you give an example?

Follow up question: what would happen if I used ZeroZero() instead of ZeroZero? Can I still compare them for equality?

Roger-luo commented 1 month ago

You only need to use ZeroZero() in the pattern match (whenever you need to construct an instance), e.g

julia> Score.ZeroZero
Main.Score.ZeroZero

julia> Score.ZeroZero()
Main.Score.var"typeof(Score)"(Main.Score.var"##Storage#ZeroZero"())

this is what I meant there is no real singleton type, it was only a syntax sugar, ZeroZero() is the actual instance.

Roger-luo commented 1 month ago

actually maybe I'll just correct your script here

module TVJulia

@data MinuteOutcome begin
    HomeGoal
    AwayGoal
    NoGoal
    BothGoal
end

@data Score begin
    ZeroZero
    ZeroOne
    OneZero
    OneOne
    Even
    Plus(Int)
    Minus(Int)
end

# const SCORES::Vector{Score.Type} = [
#     Score.ZeroZero, Score.ZeroOne, Score.OneZero, Score.OneOne, Score.Even,
#     Score.Plus(1), Score.Plus(2), Score.Plus(3), Score.Plus(4), Score.Plus(5),
#     Score.Minus(1), Score.Minus(2), Score.Minus(3), Score.Minus(4), Score.Minus(5)]
function scores() 
    Score.Type[
        Score.ZeroZero(), Score.ZeroOne(), Score.OneZero(), Score.OneOne(), Score.Even(),
        Score.Plus(1), Score.Plus(2), Score.Plus(3), Score.Plus(4), Score.Plus(5),
        Score.Minus(1), Score.Minus(2), Score.Minus(3), Score.Minus(4), Score.Minus(5)
    ]
end

function Base.:+(s::Score.Type, mo::MinuteOutcome.Type)
    @match mo begin
        MinuteOutcome.HomeGoal => 
            @match s begin
                Score.ZeroZero() => Score.OneZero()
                Score.ZeroOne()  => Score.OneOne()
                Score.OneZero()  => Score.Plus(2)
                Score.OneOne()   => Score.Plus(1)
                Score.Even()     => Score.Plus(1)
                Score.Plus(n)  => Score.Plus(min(n+1, 5))
                Score.Minus(1) => Score.Even()
                Score.Minus(n) => Score.Minus(n-1)
            end
        MinuteOutcome.AwayGoal =>
            @match s begin
                Score.ZeroZero() => Score.ZeroOne()
                Score.ZeroOne()  => Score.Minus(2)
                Score.OneZero()  => Score.OneOne
                Score.OneOne()   => Score.Minus(1)
                Score.Even()     => Score.Minus(1)
                Score.Plus(1)  => Score.Even()
                Score.Plus(n)  => Score.Plus(n-1)
                Score.Minus(n) => Score.Minus(min(n+1, 5))                
            end
        MinuteOutcome.BothGoal =>
            @match s begin
                Score.ZeroZero() => Score.OneOne()
                Score.ZeroOne()  => Score.Minus(1)
                Score.OneZero()  => Score.Plus(1)
                Score.OneOne()   => Score.Even()
                _ => s
            end
        MinuteOutcome.NoGoal() => s
    end
end
jakobjpeters commented 3 weeks ago

maybe we should just remove the singleton syntax.

That would feel more consistent :+1: