it sometimes does, but i find you have to apply it to every closure in the nested stack, and it still involves a lot of reformatting since it is incompatible with trailing closure syntax. sometimes it never really helps.
a real example, from the code that renders the swiftinit.org home page:
func body(_ body:inout HTML.ContentEncoder, format:Swiftinit.RenderFormat)
{
body[.section, { $0.class = "docs" }]
{
$0[.h2] = "Recent Docs"
$0[.ol]
{
for item:Unidoc.DB.DocsFeed.Activity<Unidoc.VolumeMetadata> in
self.docs
{
$0[.li]
{
let dynamicAge:Duration.DynamicFormat = .init(seconds: 0)
$0[.p, { $0.class = "edition"}]
// value of type 'HTML.Attribute.Factory' has no subscripts
{
$0[.span] = "\(item.volume.symbol.package)"
// cannot infer contextual base in reference to member 'span'
$0[.a]
{
$0.href = "\(Swiftinit.Docs[item.volume])"
} = item.volume.symbol.version
}
$0[.p] { $0.class = "age" } = dynamicAge.units != .seconds
? "\(dynamicAge.short) ago"
: "just now"
}
}
}
}
body[.section, { $0.class = "docs" }]
{
$0[.h2] = "Recent Docs"
$0[.ol]
{
for item:Unidoc.DB.DocsFeed.Activity<Unidoc.VolumeMetadata> in
self.docs
{
$0[.li]
{
let dynamicAge:Duration.DynamicFormat = .init(seconds: 0)
+ $0[.p, { $0.class = "edition"},
+ // value of type 'HTML.Attribute.Factory' has no subscripts
+ // cannot infer contextual base in reference to member 'p'
{
$0[.span] = "\(item.volume.symbol.package)"
$0[.a]
{
$0.href = "\(Swiftinit.Docs[item.volume])"
} = item.volume.symbol.version
+ } as (inout HTML.ContentEncoder) -> ()]
$0[.p] { $0.class = "age" } = dynamicAge.units != .seconds
? "\(dynamicAge.short) ago"
: "just now"
}
}
}
}
body[.section, { $0.class = "docs" }]
+ // generic parameter 'Renderable' could not be inferred
{
$0[.h2] = "Recent Docs"
+ // cannot infer contextual base in reference to member 'h2'
$0[.ol]
{
for item:Unidoc.DB.DocsFeed.Activity<Unidoc.VolumeMetadata> in
self.docs
{
+ $0[.li,
{
let dynamicAge:Duration.DynamicFormat = .init(seconds: 0)
$0[.p, { $0.class = "edition"},
{
$0[.span] = "\(item.volume.symbol.package)"
$0[.a]
{
$0.href = "\(Swiftinit.Docs[item.volume])"
} = item.volume.symbol.version
} as (inout HTML.ContentEncoder) -> ()]
$0[.p] { $0.class = "age" } = dynamicAge.units != .seconds
? "\(dynamicAge.short) ago"
: "just now"
+ } as (inout HTML.ContentEncoder) -> ()]
}
}
}
+ body[.section, { $0.class = "docs" },
+ type 'SVG.Embedded' has no member 'section'
{
$0[.h2] = "Recent Docs"
$0[.ol]
{
for item:Unidoc.DB.DocsFeed.Activity<Unidoc.VolumeMetadata> in
self.docs
{
$0[.li,
{
let dynamicAge:Duration.DynamicFormat = .init(seconds: 0)
$0[.p, { $0.class = "edition"},
{
$0[.span] = "\(item.volume.symbol.package)"
$0[.a]
{
$0.href = "\(Swiftinit.Docs[item.volume])"
} = item.volume.symbol.version
} as (inout HTML.ContentEncoder) -> ()]
$0[.p] { $0.class = "age" } = dynamicAge.units != .seconds
? "\(dynamicAge.short) ago"
: "just now"
} as (inout HTML.ContentEncoder) -> ()]
}
}
+ } as (inout HTML.ContentEncoder) -> ()]
+ // cannot convert value of type '(inout HTML.ContentEncoder) -> ()' to expected argument type '(inout SVG.ContentEncoder) -> ()
what was the actual problem, you wonder?
body[.section, { $0.class = "docs" }]
{
$0[.h2] = "Recent Docs"
$0[.ol]
{
for item:Unidoc.DB.DocsFeed.Activity<Unidoc.VolumeMetadata> in
self.docs
{
$0[.li]
{
let dynamicAge:Duration.DynamicFormat = .init(seconds: 0)
$0[.p, { $0.class = "edition"}]
{
$0[.span] = "\(item.volume.symbol.package)"
$0[.a]
{
$0.href = "\(Swiftinit.Docs[item.volume])"
} = item.volume.symbol.version
}
- $0[.p] { $0.class = "age" } = dynamicAge.units != .seconds
+ $0[.p] { $0.class = "age" } = dynamicAge.unit != .seconds
? "\(dynamicAge.short) ago"
: "just now"
}
}
}
}
i know this example is very specific to this particular DSL, but in the general case, it would be helpful to give the compiler “hints” about what type checking strategy to use, because it is unhelpfully exploring a lot of irrelevant possibilities here.
in particular, it should have been sufficient to explicitly-type the <li>
element, but for some reason that type annotation was not considered authoritative enough, and the compiler kept working its way up the call stack.
could i have avoided this by rigorously adhering to a coding style that always uses named parameters with explicit type annotations? probably. but that would defeat the purpose of having a DSL.