roc/test/snapshots/static_dispatch/MethodDispatch.md
Richard Feldman 641f4db0b0
Update snapshots for nominal pattern bound variable fix
The fix correctly identifies that variables bound inside nominal
patterns (like `s` in `Container.Box(s)`) are bound in the pattern
scope and don't need to be captured. This results in cleaner
canonical IR where functions that use nominal pattern matching
are now represented as pure lambdas instead of closures with
unnecessary captures.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 07:55:22 -05:00

12 KiB

META

description=Static dispatch with method calls on nominal types
type=file:Container.roc

SOURCE

Container := [Box(Str)].{
	get_value : Container -> Str
	get_value = |Container.Box(s)| s
	transform : Container, (Str -> Str) -> Container
	transform = |Container.Box(s), fn| Container.Box(fn(s))
}

# Generic function that works with any type that has a get_value method
extract : a -> Str where [a.get_value : a -> Str]
extract = |x| x.get_value()

# Generic function that works with any type that has a transform method
modify : a, (Str -> Str) -> a where [a.transform : a, (Str -> Str) -> a]
modify = |x, fn| x.transform(fn)

# Test values
container : Container
container = Container.Box("hello")

# Another container value with direct method call
myContainer : Container
myContainer = Container.Box("world")

directCall : Str
directCall = myContainer.get_value()

# Use generic functions with Container type
result1 : Str
result1 = extract(container)

result2 : Container
result2 = modify(
	container,
	|s| "${s} world",
)

main : (Str, Str, Str)
main = (directCall, result1, extract(result2))

EXPECTED

NIL

PROBLEMS

NIL

TOKENS

UpperIdent,OpColonEqual,OpenSquare,UpperIdent,NoSpaceOpenRound,UpperIdent,CloseRound,CloseSquare,Dot,OpenCurly,
LowerIdent,OpColon,UpperIdent,OpArrow,UpperIdent,
LowerIdent,OpAssign,OpBar,UpperIdent,NoSpaceDotUpperIdent,NoSpaceOpenRound,LowerIdent,CloseRound,OpBar,LowerIdent,
LowerIdent,OpColon,UpperIdent,Comma,OpenRound,UpperIdent,OpArrow,UpperIdent,CloseRound,OpArrow,UpperIdent,
LowerIdent,OpAssign,OpBar,UpperIdent,NoSpaceDotUpperIdent,NoSpaceOpenRound,LowerIdent,CloseRound,Comma,LowerIdent,OpBar,UpperIdent,NoSpaceDotUpperIdent,NoSpaceOpenRound,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound,CloseRound,
CloseCurly,
LowerIdent,OpColon,LowerIdent,OpArrow,UpperIdent,KwWhere,OpenSquare,LowerIdent,NoSpaceDotLowerIdent,OpColon,LowerIdent,OpArrow,UpperIdent,CloseSquare,
LowerIdent,OpAssign,OpBar,LowerIdent,OpBar,LowerIdent,NoSpaceDotLowerIdent,NoSpaceOpenRound,CloseRound,
LowerIdent,OpColon,LowerIdent,Comma,OpenRound,UpperIdent,OpArrow,UpperIdent,CloseRound,OpArrow,LowerIdent,KwWhere,OpenSquare,LowerIdent,NoSpaceDotLowerIdent,OpColon,LowerIdent,Comma,OpenRound,UpperIdent,OpArrow,UpperIdent,CloseRound,OpArrow,LowerIdent,CloseSquare,
LowerIdent,OpAssign,OpBar,LowerIdent,Comma,LowerIdent,OpBar,LowerIdent,NoSpaceDotLowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound,
LowerIdent,OpColon,UpperIdent,
LowerIdent,OpAssign,UpperIdent,NoSpaceDotUpperIdent,NoSpaceOpenRound,StringStart,StringPart,StringEnd,CloseRound,
LowerIdent,OpColon,UpperIdent,
LowerIdent,OpAssign,UpperIdent,NoSpaceDotUpperIdent,NoSpaceOpenRound,StringStart,StringPart,StringEnd,CloseRound,
LowerIdent,OpColon,UpperIdent,
LowerIdent,OpAssign,LowerIdent,NoSpaceDotLowerIdent,NoSpaceOpenRound,CloseRound,
LowerIdent,OpColon,UpperIdent,
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound,
LowerIdent,OpColon,UpperIdent,
LowerIdent,OpAssign,LowerIdent,NoSpaceOpenRound,
LowerIdent,Comma,
OpBar,LowerIdent,OpBar,StringStart,StringPart,OpenStringInterpolation,LowerIdent,CloseStringInterpolation,StringPart,StringEnd,Comma,
CloseRound,
LowerIdent,OpColon,OpenRound,UpperIdent,Comma,UpperIdent,Comma,UpperIdent,CloseRound,
LowerIdent,OpAssign,OpenRound,LowerIdent,Comma,LowerIdent,Comma,LowerIdent,NoSpaceOpenRound,LowerIdent,CloseRound,CloseRound,
EndOfFile,

PARSE

(file
	(type-module)
	(statements
		(s-type-decl
			(header (name "Container")
				(args))
			(ty-tag-union
				(tags
					(ty-apply
						(ty (name "Box"))
						(ty (name "Str")))))
			(associated
				(s-type-anno (name "get_value")
					(ty-fn
						(ty (name "Container"))
						(ty (name "Str"))))
				(s-decl
					(p-ident (raw "get_value"))
					(e-lambda
						(args
							(p-tag (raw ".Box")
								(p-ident (raw "s"))))
						(e-ident (raw "s"))))
				(s-type-anno (name "transform")
					(ty-fn
						(ty (name "Container"))
						(ty-fn
							(ty (name "Str"))
							(ty (name "Str")))
						(ty (name "Container"))))
				(s-decl
					(p-ident (raw "transform"))
					(e-lambda
						(args
							(p-tag (raw ".Box")
								(p-ident (raw "s")))
							(p-ident (raw "fn")))
						(e-apply
							(e-tag (raw "Container.Box"))
							(e-apply
								(e-ident (raw "fn"))
								(e-ident (raw "s"))))))))
		(s-type-anno (name "extract")
			(ty-fn
				(ty-var (raw "a"))
				(ty (name "Str")))
			(where
				(method (module-of "a") (name "get_value")
					(args
						(ty-var (raw "a")))
					(ty (name "Str")))))
		(s-decl
			(p-ident (raw "extract"))
			(e-lambda
				(args
					(p-ident (raw "x")))
				(e-field-access
					(e-ident (raw "x"))
					(e-apply
						(e-ident (raw "get_value"))))))
		(s-type-anno (name "modify")
			(ty-fn
				(ty-var (raw "a"))
				(ty-fn
					(ty (name "Str"))
					(ty (name "Str")))
				(ty-var (raw "a")))
			(where
				(method (module-of "a") (name "transform")
					(args
						(ty-var (raw "a"))
						(ty-fn
							(ty (name "Str"))
							(ty (name "Str"))))
					(ty-var (raw "a")))))
		(s-decl
			(p-ident (raw "modify"))
			(e-lambda
				(args
					(p-ident (raw "x"))
					(p-ident (raw "fn")))
				(e-field-access
					(e-ident (raw "x"))
					(e-apply
						(e-ident (raw "transform"))
						(e-ident (raw "fn"))))))
		(s-type-anno (name "container")
			(ty (name "Container")))
		(s-decl
			(p-ident (raw "container"))
			(e-apply
				(e-tag (raw "Container.Box"))
				(e-string
					(e-string-part (raw "hello")))))
		(s-type-anno (name "myContainer")
			(ty (name "Container")))
		(s-decl
			(p-ident (raw "myContainer"))
			(e-apply
				(e-tag (raw "Container.Box"))
				(e-string
					(e-string-part (raw "world")))))
		(s-type-anno (name "directCall")
			(ty (name "Str")))
		(s-decl
			(p-ident (raw "directCall"))
			(e-field-access
				(e-ident (raw "myContainer"))
				(e-apply
					(e-ident (raw "get_value")))))
		(s-type-anno (name "result1")
			(ty (name "Str")))
		(s-decl
			(p-ident (raw "result1"))
			(e-apply
				(e-ident (raw "extract"))
				(e-ident (raw "container"))))
		(s-type-anno (name "result2")
			(ty (name "Container")))
		(s-decl
			(p-ident (raw "result2"))
			(e-apply
				(e-ident (raw "modify"))
				(e-ident (raw "container"))
				(e-lambda
					(args
						(p-ident (raw "s")))
					(e-string
						(e-string-part (raw ""))
						(e-ident (raw "s"))
						(e-string-part (raw " world"))))))
		(s-type-anno (name "main")
			(ty-tuple
				(ty (name "Str"))
				(ty (name "Str"))
				(ty (name "Str"))))
		(s-decl
			(p-ident (raw "main"))
			(e-tuple
				(e-ident (raw "directCall"))
				(e-ident (raw "result1"))
				(e-apply
					(e-ident (raw "extract"))
					(e-ident (raw "result2")))))))

FORMATTED

NO CHANGE

CANONICALIZE

(can-ir
	(d-let
		(p-assign (ident "Container.get_value"))
		(e-lambda
			(args
				(p-nominal
					(p-applied-tag)))
			(e-lookup-local
				(p-assign (ident "s"))))
		(annotation
			(ty-fn (effectful false)
				(ty-lookup (name "Container") (local))
				(ty-lookup (name "Str") (builtin)))))
	(d-let
		(p-assign (ident "Container.transform"))
		(e-lambda
			(args
				(p-nominal
					(p-applied-tag))
				(p-assign (ident "fn")))
			(e-nominal (nominal "Container")
				(e-tag (name "Box")
					(args
						(e-call
							(e-lookup-local
								(p-assign (ident "fn")))
							(e-lookup-local
								(p-assign (ident "s"))))))))
		(annotation
			(ty-fn (effectful false)
				(ty-lookup (name "Container") (local))
				(ty-parens
					(ty-fn (effectful false)
						(ty-lookup (name "Str") (builtin))
						(ty-lookup (name "Str") (builtin))))
				(ty-lookup (name "Container") (local)))))
	(d-let
		(p-assign (ident "extract"))
		(e-lambda
			(args
				(p-assign (ident "x")))
			(e-dot-access (field "get_value")
				(receiver
					(e-lookup-local
						(p-assign (ident "x"))))
				(args)))
		(annotation
			(ty-fn (effectful false)
				(ty-rigid-var (name "a"))
				(ty-lookup (name "Str") (builtin)))
			(where
				(method (ty-rigid-var-lookup (ty-rigid-var (name "a"))) (name "get_value")
					(args
						(ty-rigid-var-lookup (ty-rigid-var (name "a"))))
					(ty-lookup (name "Str") (builtin))))))
	(d-let
		(p-assign (ident "modify"))
		(e-lambda
			(args
				(p-assign (ident "x"))
				(p-assign (ident "fn")))
			(e-dot-access (field "transform")
				(receiver
					(e-lookup-local
						(p-assign (ident "x"))))
				(args
					(e-lookup-local
						(p-assign (ident "fn"))))))
		(annotation
			(ty-fn (effectful false)
				(ty-rigid-var (name "a"))
				(ty-parens
					(ty-fn (effectful false)
						(ty-lookup (name "Str") (builtin))
						(ty-lookup (name "Str") (builtin))))
				(ty-rigid-var-lookup (ty-rigid-var (name "a"))))
			(where
				(method (ty-rigid-var-lookup (ty-rigid-var (name "a"))) (name "transform")
					(args
						(ty-rigid-var-lookup (ty-rigid-var (name "a")))
						(ty-parens
							(ty-fn (effectful false)
								(ty-lookup (name "Str") (builtin))
								(ty-lookup (name "Str") (builtin)))))
					(ty-rigid-var-lookup (ty-rigid-var (name "a")))))))
	(d-let
		(p-assign (ident "container"))
		(e-nominal (nominal "Container")
			(e-tag (name "Box")
				(args
					(e-string
						(e-literal (string "hello"))))))
		(annotation
			(ty-lookup (name "Container") (local))))
	(d-let
		(p-assign (ident "myContainer"))
		(e-nominal (nominal "Container")
			(e-tag (name "Box")
				(args
					(e-string
						(e-literal (string "world"))))))
		(annotation
			(ty-lookup (name "Container") (local))))
	(d-let
		(p-assign (ident "directCall"))
		(e-dot-access (field "get_value")
			(receiver
				(e-lookup-local
					(p-assign (ident "myContainer"))))
			(args))
		(annotation
			(ty-lookup (name "Str") (builtin))))
	(d-let
		(p-assign (ident "result1"))
		(e-call
			(e-lookup-local
				(p-assign (ident "extract")))
			(e-lookup-local
				(p-assign (ident "container"))))
		(annotation
			(ty-lookup (name "Str") (builtin))))
	(d-let
		(p-assign (ident "result2"))
		(e-call
			(e-lookup-local
				(p-assign (ident "modify")))
			(e-lookup-local
				(p-assign (ident "container")))
			(e-lambda
				(args
					(p-assign (ident "s")))
				(e-string
					(e-literal (string ""))
					(e-lookup-local
						(p-assign (ident "s")))
					(e-literal (string " world")))))
		(annotation
			(ty-lookup (name "Container") (local))))
	(d-let
		(p-assign (ident "main"))
		(e-tuple
			(elems
				(e-lookup-local
					(p-assign (ident "directCall")))
				(e-lookup-local
					(p-assign (ident "result1")))
				(e-call
					(e-lookup-local
						(p-assign (ident "extract")))
					(e-lookup-local
						(p-assign (ident "result2"))))))
		(annotation
			(ty-tuple
				(ty-lookup (name "Str") (builtin))
				(ty-lookup (name "Str") (builtin))
				(ty-lookup (name "Str") (builtin)))))
	(s-nominal-decl
		(ty-header (name "Container"))
		(ty-tag-union
			(ty-tag-name (name "Box")
				(ty-lookup (name "Str") (builtin))))))

TYPES

(inferred-types
	(defs
		(patt (type "Container -> Str"))
		(patt (type "Container, (Str -> Str) -> Container"))
		(patt (type "a -> Str where [a.get_value : a -> Str]"))
		(patt (type "a, (Str -> Str) -> a where [a.transform : a, (Str -> Str) -> a]"))
		(patt (type "Container"))
		(patt (type "Container"))
		(patt (type "Str"))
		(patt (type "Str"))
		(patt (type "Container"))
		(patt (type "(Str, Str, Str)")))
	(type_decls
		(nominal (type "Container")
			(ty-header (name "Container"))))
	(expressions
		(expr (type "Container -> Str"))
		(expr (type "Container, (Str -> Str) -> Container"))
		(expr (type "a -> Str where [a.get_value : a -> Str]"))
		(expr (type "a, (Str -> Str) -> a where [a.transform : a, (Str -> Str) -> a]"))
		(expr (type "Container"))
		(expr (type "Container"))
		(expr (type "Str"))
		(expr (type "Str"))
		(expr (type "Container"))
		(expr (type "(Str, Str, Str)"))))