I'm working on day 4 and can't figure out a type error
The full wip code is here https://github.com/mulias/roctoberfest/blob/main/advent_2022/day_04/main.roc
The relevant parts are
rangeParser : Parser RawStr (Range Nat)
rangeParser =
const (\first -> \last -> Range.new first last)
|> keep digits
|> skip (codeunit '-')
|> keep digits
# Copied from roc-parser for debugging
digits : Parser RawStr Nat
digits =
oneOrMore digit
|> map \ds -> List.walk ds 0 (\sum, d -> sum * 10 + d)
Range is defined in a different module
Range a : { first : Num a, last : Num a } where a implements Bool.Eq
new : Num a, Num a -> Range a
new = \a, b -> { first: a, last: b }
The error is
── TYPE MISMATCH ──────────────────────────────────────────────────── main.roc ─
Something is off with the body of the rangeParser definition:
50│ rangeParser : Parser RawStr (Range Nat)
51│ rangeParser =
52│> const (\first -> \last -> Range.new first last)
53│> |> keep digits
54│> |> skip (codeunit '-')
55│> |> keep digits
This Core.keep call produces:
Parser (List (Int Unsigned8)) (Range (Integer Natural))
But the type annotation on rangeParser says it should be:
Parser (List (Int Unsigned8)) (Range Nat)
Am I using Num
wrong? I'm confused that digits : Parser RawStr Nat
seems correctly typed, but something is going wrong with keep
?
keep is
keep : Parser input (a -> b), Parser input a -> Parser input b
Now I'm just rubber ducking :)
I tried typing the curried function
rangeBuilder : Nat -> Nat -> Range Nat
rangeBuilder = \first -> \last -> Range.new first last
but that's giving me
── NOT END OF FILE ────────────────────────────────────────────────── main.roc ─
I expected to reach the end of the file, but got stuck here:
50│ rangeBuilder : Nat -> Nat -> Range Nat
^
Can you try:
Range a : { first : a, last : a } where a implements Bool.Eq
new : Num a, Num a -> Range (Num a)
new = \a, b -> { first: a, last: b }
Also, just a note on the currying, you need parens in roc:
rangeBuilder : Nat -> (Nat -> Range Nat)
Thanks for the hint, I'm getting slightly different errors now which I can bang on.
Is there going to be a separate ability for Ord
or something similar? That implementation of Range
doesn't seem right with just Eq
I'm not sure. No one has talked about it. Sounds reasonable to have. Also, I guess your first version should probably work, though not sure what exactly is going on.
I think richard has mentioned Sort
ability which is similar to Rust's Ord
@Richard Feldman mentions it in the Abilities proposal:
I prefer the name Sort because I think it should only be for sorting and not general ordering (e.g. I think the <, >, <=, and >= operators should continue to only accept numbers, not other sortable types like strings and booleans)
But that's not implement yet right?
I tried following the errors and changed all the types from Range Nat
to Range (Integer Natural)
, and I got a compiler exception :upside_down: so I guess I'll report that. Probably should just specialize the Range struct instead of making it polymorphic
Yeah that's right. It's not in the builtins and I don't think anyone has started on it. Not sure if there is a more concrete proposal anywhere.
What about something like Range U32
?
── TYPE MISMATCH ──────────────────────────────────────────────────── main.roc ─
Something is off with the body of the rangeParser definition:
52│ rangeParser : Parser RawStr (Range U32)
53│ rangeParser =
54│> const (\first -> \last -> Range.new first last)
55│> |> keep digits
56│> |> skip (codeunit '-')
57│> |> keep digits
This Core.keep call produces:
Parser (List (Int Unsigned8)) (Range (Integer Natural))
But the type annotation on rangeParser says it should be:
Parser (List (Int Unsigned8)) (Range U32)
I tried
rangeParser : Parser RawStr (Range U32)
rangeParser =
const (\first -> \last -> Range.new (Num.intCast first) (Num.intCast last))
|> keep digits
|> skip (codeunit '-')
|> keep digits
and got
── TYPE MISMATCH ──────────────────────────────────────────────────── main.roc ─
Something is off with the body of the rangeParser definition:
52│ rangeParser : Parser RawStr (Range U32)
53│ rangeParser =
54│> const (\first -> \last -> Range.new (Num.intCast first) (Num.intCast last))
55│> |> keep digits
56│> |> skip (codeunit '-')
57│> |> keep digits
This Core.keep call produces:
Parser (List (Int Unsigned8)) (Range (Integer b)) where b implements Eq
But the type annotation on rangeParser says it should be:
Parser (List (Int Unsigned8)) (Range U32)
Have you got roc check Range.roc
happy?
I got it working fine with Range : { first : Nat, last : Nat }
, so at least there's that. I'll go back and see
@Luke Boswell roc check Range.roc
came back clean, so it seems like Range
is internally consistent.
I don't know if this helps, but modified to an Opaque type and gave it the Eq
ability
interface Range exposes [Range, new, min, max, intersection, isContained] imports []
Range := { first : U32, last : U32 } implements [Eq { isEq: rangeIsEq }]
rangeIsEq : Range, Range -> Bool
rangeIsEq = \@Range ra, @Range rb -> ra == rb
new : U32, U32 -> Range
new = \first, last -> @Range { first, last }
min : Range -> U32
min = \@Range { first, last } -> if first <= last then first else last
max : Range -> U32
max = \@Range { first, last } -> if first <= last then last else first
intersection : Range, Range -> [Disjoint, Intersecting (Range)]
intersection = \rangeA, rangeB ->
if min rangeA > max rangeB || min rangeB > max rangeA then
Disjoint
else
first = Num.max (min rangeA) (min rangeB)
last = Num.min (max rangeA) (max rangeB)
Intersecting (new first last)
expect intersection (new 0 5) (new 1 3) == Intersecting (new 1 3)
isContained : Range, Range -> Bool
isContained = \inner, outer ->
intersection inner outer == Intersecting inner
expect new 1 3 == @Range {first: 1, last: 3}
Also works ok with Nat
which is probably a nicer integration with the parser
ohhh nice. I would expect that to work, since Range
is no longer polymorphic. But I briefly tried to make an opaque type and couldn't get the syntax right so that helps
I've played around with it a bit and I am not sure if there is any way to constrain that type variable to be an Integer or Number. @Ayaz Hafiz could probably advise on that??
numbers are def not the only thing you could have a range of, so I think that's fine. What I would really want would be an ability for comparison.
Yeah unfortunately there is no way to constrain a type variable to be numeric. I think this should all work correctly if the appropriate abilities were available.
That makes sense! The errors were confusing but I've learned something.
Day 4 https://github.com/mulias/roctoberfest/tree/main/advent_2022/day_04
Last updated: Jul 06 2025 at 12:14 UTC