-
Notifications
You must be signed in to change notification settings - Fork 23
[RFC] Allow $
to be override-able for custom types
#139
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Why do you say this?
I have no issue in importing # foos.nim
type Foo* = object
a*: int
proc `$`*(f: Foo): string = "hello" and then import foos
let x = Foo(a: 3)
echo x # hello Even moving the |
That's what I do too though I tend to use more descriptive names. Usually these grow a |
I don't see an RFC here. |
The RFC is either about removing system.$ for objects or to turn $ into a type-bound operation. Which would make sense for |
well, what is called |
I fail to see how this solves much; all specializations would still be tied to a scope, not to a type. EDIT: I stand corrected, C++ continues to surprise. |
I don't exactly know what you mean with "tied to a scope", but only the specialization needs to be able to see the generic procedure to be specialized. Other modules don't need to see the specialized function it can be written in an arbitrary cpp file, nothing needs to be added to a header to implement the specialization. |
I grappled with this "overload visibility problem" myself in the context of our serialization library. Just like It feels a bit unfortunate to give up on the "working by default" tenet of the library, so I came up with an alternative idea that might make sense. What if we expand the meaning of Please note that any solution that requires the type bound operation to be defined in the module of the type itself doesn't quite work for the serialization library, because the author of the type could not possibly imagine all the possible serialization formats that type could support. Defining serialization methods for types in 3rd party libraries is already common-place in our code. |
But like multi-methods, this is a non-modular, global mechanism tying us further to the current "always compile the full project" design.
Well that would be exactly what I would propose anyway. It works well enough for every mainstream programming language that I've ever used -- and they can all do partial rebuilds / incremental compilations too, this is not a coincidence. |
Er, I'm not sure my comment was read properly :) Of course, I can just give up on the notion of default serialization and require that a proper overload is always in scope where needed, but I don't think that having a little bit of full-program-dependant codegen is such a deal breaker. It just needs to be kept reasonably small and efficiently updated when it needs to change. Multi-staged programming based on global compile-time variables will need similar mechanisms eventually. |
How about a pragma which defines "standard" functions:
The edit: forgot to mention that the goal of this approach is to clearly mark an often overloaded function's main implementation as something important to the business logic. |
To make a final point about template specializations. They are exactly what would be the solution to our problem. As a proof, I made an example in C++ that uses template specialization to solve exactly this problem: main.cpp #include "system.hpp"
// extendable for user types (unlike printf)
struct MyStruct {
float a,b,c;
};
template <>
void dollar(FILE* out, const MyStruct& arg) {
fprintf(out, "[%f, %f, %f]", arg.a, arg.b, arg.c);
}
struct UnknownStruct {};
int main() {
// specialization in main.cpp
MyStruct ms = {1,2,3};
printLine(stdout, "Hallo ", 123, " ", ms);
// specialization in system.cpp, no exposure in system.hpp
SystemStruct tmp = {"Egon", 456789};
printLine(stdout, tmp);
// fallback to the default implementation from the header
UnknownStruct us;
printLine(stdout, us);
} system.hpp #pragma once
#include <cstdio>
#include <utility>
#include <typeinfo>
template <typename T>
void dollar(FILE* out, const T& arg) {
fprintf(out, "--%s--", typeid(arg).name());
}
// this is a normal overload, not a template specialization.
static inline void dollar(FILE* out, const char* arg) {
fprintf(out, arg);
}
struct SystemStruct {
const char* name;
int value;
};
static inline void printLine(FILE* out) {
fprintf(out, "\n");
fflush(out);
}
template <typename Arg, typename... Args>
void printLine(FILE* out, Arg&& arg, Args&&... args)
{
dollar(out, std::forward<Arg>(arg));
printLine(out, std::forward<Args>(args)...);
} system.cpp #include "system.hpp"
using cstring = const char*;
// the empty template parameter list signals that this is a template
// specialization, not an overload.
template <>
void dollar(FILE* out, const int& arg) {
fprintf(out, "%d", arg);
}
template <>
void dollar(FILE* out, const float& arg) {
fprintf(out, "%f", arg);
}
template <>
void dollar(FILE* out, const SystemStruct& arg) {
fprintf(out, "[name: %s value:%d]", arg.name, arg.value);
}; Makefile: all: a.out
main.o: main.cpp system.hpp
c++ -c main.cpp
system.o: system.cpp system.hpp
c++ -c system.cpp
a.out: main.o system.o
c++ main.o system.o output:
|
This RFC is stale because it has been open for 1095 days with no activity. Contribute a fix or comment on the issue, or it will be closed in 7 days. |
This was implemented as objectdollar #475 also covers the case of |
Consider this maybe a saner version of #8023. From Araq's comment in that thread:
I myself have had this problem multiple times -
$
is desirable to re-implement in a number of cases wheresystem.$
would not produce a useful result - for instance, objects containing{.importc.}
types as fields. One can make a new$
procedure for a type in a module now, sure, and those types will use that new procedure when stringified - only when such stringifications occur inside that module, which is mostly useless. If you import that module and use$
anywhere, we will still always usesystem.$
. The export marker makes no difference.There is a way you can override
$
and have it work when imported - make the type in question distinct. I have tried this, and it's a silly solution.The alternative, I guess, is to just suck it up and make a
toString()
proc for a given type and call that manually with the extra explicitness that incurs.The text was updated successfully, but these errors were encountered: