The following compiles and can be imported from another module:
type Obj = object
x, y: int
proc a*(obj: Obj) = discard
proc a*: Obj = Obj(x: 3, y: 4)
In my opinion, this should not be allowed in either case. You could argue for the second proc to give a warning and the first one to give an error however. Real world case related to this
Actually, I think this is good. The return type becomes an opaque type, on which you can only act through the interface exposed by the module. I think this can actually be useful and intentional
Such an opaque type is common in C, but not because the type is undeclared (it's just undefined). Nim has privacy specifications (only those members with *) are exported, so the issue of reading fields is solved.
However, I can see how this would be useful for an interface that provides an unconstructable object. Only the implementation would be able to return it. The problem is, though, how do these things get composed into other objects?? The object type name iteself is not visible - so can they only be used with let?
As an example, the following code works:
main.nim
import module
proc main() =
let instance: MyObj = initMyObj(52.0)
echo instance
when isMainModule:
main()
module.nim
type
MyObj* = object
x: float
proc initMyObj*(x: float): MyObj =
MyObj(x: x)
proc `$`*(m: MyObj): string =
'(' & $m.x & ')'
However, if I change MyObj* = object to the unexported version, MyObj = object, then it is not clear by what type I can bind the object.
let instance: auto = initMyObj(52.0)
will work just fine, but it is not clear how this can integrate with composition. For example:
# This code in main.nim
type
Encapsulates = object
field: MyObj
would work if the type MyObj were to be exported, but not otherwise. Maybe it is useful if someone wants their object not to be stored inside another object, but I've never had such a restriction myself.
I can envision it though: if the address of the instance it returns is important. Maybe you're working in an environment where you have memory mapped to the object, and you're using it as a safe handle. That use case could be helpful. Or maybe the object must always be referenced through a handle. In that case, though, this opaque type must not be copyable (and that should be enforced).
Useful or not, the compiler is consistent with the spec here and the problem is minor, only affecting insufficiently tested code. Closing.
Most helpful comment
Actually, I think this is good. The return type becomes an opaque type, on which you can only act through the interface exposed by the module. I think this can actually be useful and intentional