Skip to content

Type unions concept #921

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Jun 4, 2025
Merged

Type unions concept #921

merged 31 commits into from
Jun 4, 2025

Conversation

colinleach
Copy link
Contributor

I'm hoping that gleam/role-playing-game can be adapted to pair with this.

Though this concept is relatively short and simple, it's been in the works for a couple of weeks. I had some medical stuff going on (yet again).

Now I'm (hopefully) back in action, I'll try and put together the long-awaited Multiple Dispatch concept, to round out the group on types. Once I have all the concept docs, I'll go back and work on the exercises.

Colin Leach added 28 commits April 2, 2025 15:14
@depial
Copy link
Contributor

depial commented May 16, 2025

Now I'm (hopefully) back in action

Good to hear you're well and back in action! On my end, I currently have decent availability, but poor internet. Starting May 24th (ish), I should have decent internet, but likely more limited availability. So, potentially not much change in the near future, but I'll continue to try to keep up. That said, I'll see if I can come up with a first draft of an exercise for the Types concept in the coming week...

Though this concept is relatively short and simple

On a first look through this, it looked good and I didn't catch anything to note. Where a suggestion popped up, I found it already incorporated later on :)

@colinleach
Copy link
Contributor Author

Thanks for the update. Meanwhile, I have plenty to keep me busy, even if you aren't able to review!

@depial depial mentioned this pull request May 18, 2025
Copy link
Contributor

@depial depial left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a small subject so I don't see anything to add at present, but I'll keep my eyes open. Everything else looks good!

@depial
Copy link
Contributor

depial commented Jun 4, 2025

I've had a look at the Gleam exercise you mentioned. I could try adapting that if you haven't gotten into it as I should have pretty good availability for the next week.

I'm curious what prereqs you were thinking. The about.md mentions multiple dispatch as a future concept, but I think we could still use function signatures since there is an example of that. Not sure if you have any other thoughts?

@colinleach
Copy link
Contributor Author

colinleach commented Jun 4, 2025

It's a couple of weeks since I looked at role-playing-game (I got fed up with it and worked on errors instead). All I can find is this initial code:

struct Player
    name::Union{String, missing}
    level::Int64
    health::Int64
    mana::Union{Int64, nothing}
end

introduce(player::Player) = ismissing(player.name) ? "Mighty Magician" : player.name

function revive(player::Player)
    player.health > 0 || return nothing

    player.level >= 10 ? Player(player.name, player.level, player.health, 100) : 
                         Player(player.name, player.level, 100, player.mana)
end

function cast_spell(player::Player, cost::Int64)
    player.mana < cost && return (player, 0)

    isnothing(player.mana) &&  
        return (Player(player.name, player.level, player.health - cost, player.mana), 0)
 
    (Player(player.name, player.level, player.health - damage, player.mana - cost), cost * 2)
end

I don't know if it's any use, but feel free to use it or scrap it.

My views on prereqs changed since I submitted the draft PR. I see this as needing parametric-types but not multiple-dispatch.

@depial
Copy link
Contributor

depial commented Jun 4, 2025

Great! I'll give it a look

@colinleach
Copy link
Contributor Author

I need to do more on the Julia syllabus, now you're back. I felt I needed a break for a few days, doing other stuff.

Over the weekend, I wrote a few things for Kotlin, which ended up getting exactly zero response. Yesterday (between the psychodramas!) I started working through the Gleam syllabus, to see what it looks like from a student perspective. It's a few years since I did some exercises on this track, and the syllabus didn't exist then. Nice language, but I don't really have a use case for the BEAM.

@depial
Copy link
Contributor

depial commented Jun 4, 2025

I'll be happy to see what we can get done in the next week if your up for it!

After that, my availability may take a nose dive again for a couple of weeks, but I'll try as ever to stay in touch with any developments.

@colinleach
Copy link
Contributor Author

I just found one more bit, in runtests.jl

using Test 

include("role-playing-game.jl")

@testset verbose = true "tests" begin
    @testset "1. introduce" begin
        @testset "With their own name" begin
            player = Player(name = "Gandalf", level = 1, health = 42, mana = nothing)
        end

    end
end

It was at that point that I realized I needed to rewrite the exemplar code to use @kwdef. Bad timing, so I went and cooked dinner, then failed to come back to it.

It was one of those days...

@depial
Copy link
Contributor

depial commented Jun 4, 2025

Along with the @kwdef, should we go ahead an use a mutable struct?

A first thought: When I first saw the Gleam version, I thought it could be nice to introduce two methods with different function signatures (which would give a look ahead to multiple dispatch without explaining anything). For example increment(level::Int, multiplier::Int) which could increase the level and increment(mana::Union{Int64, nothing}, level::Int) which would could increment the mana in the revive function. I think this would require a modification of the story to include a new function for leveling up, but I don't think it would be hard. Here's a quick mock up of exemplar.jl

@kwdef mutable struct Player
    name::Union{String, missing}
    level::Int
    health::Int
    mana::Union{Int, nothing}
end

introduce(player::Player) = ismissing(player.name) ? "Mighty Magician" : player.name

increment(mana::Union{Int, nothing}, level::Int) = level  10 ? 100 : mana
increment(level::Int, multiplier::Int) = min(level + multiplier, 42)

function revive(player::Player)
    player.health > 0 || return nothing
    player.health = 100
    player.mana = increment(player.mana, player.level)
end

function castspell(player::Player, cost::Int)
    if isnothing(player.mana)
        player.health -= cost
        player, 0
    elseif player.mana < cost 
        player, 0
    else
        player.mana -= cost
        player, 2cost
    end
end

function levelup(player::Player, increase::Int)
    multiplier = player.mana  100 ? 2 : 1
    player.level = increment(player.level, multiplier)
end

A potential stub could look like:

@kwdef mutable struct Player

end

function introduce(player::Player)
    
end

function increment(mana::Union{Int, nothing}, level::Int)
    
end

function increment(level::Int, multiplier::Int)
    
end

function revive(player::Player)
    
end

function castspell(player::Player, cost::Int)
    
end

function levelup(player::Player, increase::Int)
    
end

With this, it would be also possible to have the student name the type union IntOrNothing = Union{Int, nothing}, for use in the struct and the function.
That stub could look like:

IntOrNothing = # define type union

@kwdef mutable struct Player

end

function introduce(player::Player)
    
end

function increment(mana::IntOrNothing, level::Int)
    
end

function increment(level::Int, multiplier::Int)
    
end

function revive(player::Player)
    
end

function castspell(player::Player, cost::Int)
    
end

function levelup(player::Player, increase::Int)
    
end

Let me know what you think about anything I've floated here. In particular, I'm not sure if this is an appropriate way to hint at multiple dispatch or if it's too advanced or whatnot.

@colinleach
Copy link
Contributor Author

Yes! This is the sort of fresh thinking the exercise needed - a straight port from Gleam wasn't satisfying me.

Go for it, and good luck!

@colinleach
Copy link
Contributor Author

I see this as needing parametric-types but not multiple-dispatch

I should have said this is entirely flexible. It could go below multiple-dispatch or alongside, depending how the exercise ends up.

@depial
Copy link
Contributor

depial commented Jun 4, 2025

depending how the exercise ends up.

Agreed!

At present, personally, I would like to see it beside multiple-dispatch, more than below.

Also, if we wanted to push it a bit more as a hint at multiple-dispatch we could even use composite-types as the prereq to inch it up the tree (since I had no plans for anything parametric).

@colinleach colinleach marked this pull request as ready for review June 4, 2025 23:31
@colinleach colinleach merged commit e638e5f into exercism:main Jun 4, 2025
1 check passed
@colinleach
Copy link
Contributor Author

I've gone ahead and merged this, accepting that it may need a few changes later to align with the exercise.

@colinleach colinleach deleted the type-unions branch June 8, 2025 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants