Sorry if this is a noob question, but I dug into documentation and I couldn't find answers for this problem I'm having.
Can anyone help?
type
TView = proc(name: string): string {.cdecl.}
TPattern = tuple
prefix: string
view: TView
TPatterns = seq[TPattern]
proc foobar(name: string): string =
return "<b>" & name & "</b>"
var patterns: TPatterns
patterns = @[
(prefix: "/", view: foobar)
]
# Error: type mismatch:
# got (seq[tuple[prefix: string, view: proc (string): string]])
# but expected 'TPatterns'
type
TResponse = ref object {.inheritable.}
TStringResponse = ref object of TResponse
text: string
TView = proc(name: string): TResponse {.cdecl.}
TPattern = tuple
prefix: string
view: TView
TPatterns = seq[TPattern]
proc home(name: string): TStringResponse {.cdecl.} =
return TStringResponse(text: "<b>" & name & "</b>")
var patterns: TPatterns
# Error: type mismatch:
# got (seq[tuple[prefix: string, view: proc (string): TStringResponse{.cdecl.}]])
# but expected 'TPatterns'
patterns = @[
(prefix: "/", view: home)
]
for p in patterns:
echo(p.view("Home!").text)
In fact, "home" returns TStringResponse that inherits from TResponse. Isn't this expected to work?
You can fix part of the problem by casting home to TView, i.e.:
patterns = @[
(prefix: "/", view: TView(home))
]
The cast works because (in my experience, I haven't seen it documented anywhere) Nimrod allows covariance for results. You do have to make it explicit, though.
Note that in the last part of your code, p.view("Home!") will return a TResponse rather than a TStringResponse result, which doesn't have a text attribute. You can downcast to a TStringResponse for the purpose of this example, of course:
for p in patterns:
let resp = p.view("Home!")
if resp of TStringResponse:
echo TStringResponse(resp).text
More generally, you probably want methods that work appropriately for each subtype of TResponse.
let resp = p.view("Home!")
if resp of TStringResponse:
echo TStringResponse(resp).text
I need to cast resp to TStringResponse even though "resp of TStringResponse" being true?
That's the runtime type that the compiler doesn't know about, so it can't make the decision at compile time.
Edit: Note that this is just an ad-hoc hack. In practice, you'll want to use methods in conjunction with subtyping, e.g.:
method printme(self: TResponse) =
echo "Base type, nothing to see here."
method printme(self: TStringResponse) =
echo self.text
for p in patterns:
p.view("Home!").printme
Okay, it worked fine!
Now my only concern with all this code that we discussed is the need to cast TView:
patterns = @[
(prefix: "/", view: TView(home))
]
Since this was already done in tuple definition:
TPattern = tuple
prefix: string
view: TView
but.. it works, thanks!
4. Use this solution:
type
PResponse = ref object {.inheritable.}
PStringResponse = ref object of PResponse
text: string
TView = proc(name: string): PResponse {.cdecl.}
TPattern = tuple
prefix: string
view: TView
TPatterns = seq[TPattern]
method getText(r: PResponse): string = ""
method getText(r: PStringResponse): string = r.text
proc home(name: string): PResponse {.cdecl.} =
return PStringResponse(text: "<b>" & name & "</b>")
var patterns: TPatterns
patterns = @[
(prefix: "/", view: home)
]
for p in patterns:
echo(p.view("Home!").getText)
But I wouldn't use inheritance at all. ;-)