[ https://issues.apache.org/jira/browse/THRIFT-1871?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13601077#comment-13601077 ]
Eirik Sletteberg commented on THRIFT-1871: ------------------------------------------ Maybe it's not very clear, but I'm actually talking about two different, but related issues: 1) Improving the way clients handle optional fields 2) A proposed improvent to the IDL so that functions may be marked as optional/nullable I'll argument for (1) first: Here's a new, slighty modified example where not all users have specified their age. In java, you would do something like this: Thrift IDL: ------- struct User { 1: i3d id; 2: optional i32 born; 3: string name; } service UserService { User getUserById(1: i32 id); } Java code: ------ User user = myThriftClient.getUserById(123); int age = 2013 - user.getBorn(); // If age is optional and not specified, returns zero System.out.println("The user is "+age+" years old"); ------- The problem is; the java code compiles, but gives us weird errors. If a user doesn't have age specified, it would print "The user is 2013 years old", and not "age unknown". Say we generated java code with a new compiler flag that says "use-option-wrappers-for-optional-fields". Then we would get: ---------- User user = myThriftClient.getUserById(123); int age = 2013 - user.getBorn(); // Compiler error: Class Option<Integer> cannot be cast to type integer (or something like that) ---------- This forces the developer to do the null check, for example like this: ---------- User user = myThriftClient.getUserById(123); if (user.getBorn() instance of Some<int>) { int age = 2013 - user.getBorn().get(); System.out.println("The user is "+age+" years old.); } else { System.out.println("Age not specified."); } ---------- Of course, if everybody remembers to do their null checks everytime, this won't be a problem, but the fact is we are humans, we forget null checks, and type safety is here for our rescue. The more errors our compiler can catch, the less errors will go into production. "could the protocols handle these optional results?" I'm thinking of implementing this as a backwards-compatible change that has no impact on the protocols/transports. As of today, the protocols handle passing of null values. The difference lies in the clients' generated code - as of today, if null values are passed in, developers have to remember their null checks. With a new compiler flag (specifiyng use of options), one would get generated code that uses Option wrappers for optional fields - then the compiler will catch the errors. "And how would you implement this for all other target languages?" I would say we could add a compiler flag "use-option-class-for-optional-fields" or something like that. If this flag is set, compilers will generate code that has null-safe handling of optional fields, if not, the code generator will not accept this flag. I believe most languages supported by Thrift have the ability to implement Option structs in some way or another. (Again; if not, the code generator doesn't allow this flag and generates code for optional fields just the same way it does today.) Regarding case nr. 2: "Moreover, I don't really see what's the big deal of using either exceptions or optional fields to handle your example." In the case that a user with the given ID doesn't exist, one can solve it by throwing an exception, but that kind of violates the "principle" only use exceptions for exceptional conditions? And as of today, all functions are allowed to return null. So if a user is not found, and the server is implemented to return null, then a client will get a (unexpected?) NullPointerException (or the equalent in other languages). > Better null-safe handling > ------------------------- > > Key: THRIFT-1871 > URL: https://issues.apache.org/jira/browse/THRIFT-1871 > Project: Thrift > Issue Type: Brainstorming > Components: Compiler (General) > Reporter: Eirik Sletteberg > Attachments: Add_nullable_modifier_to_functions.patch > > > Optional fields are not really handled well, especially in java clients. > (NullPointerExceptions everywhere) > Scrooge (Twitter's alternative Thrift compiler implementation) solves this > problem by wrapping all optional fields in an Option<T> class. Could this be > implemented in Thrift? > As of today, services have no way of returning empty results; for example > UserService { > 1: User getUser(1: i32 id) > } > cannot return an empty user if the ID is not found. > There are a few ways to solve this: > UserService { > 1: User getUser(1: i32 id) throws UserNotFoundException > } > -- or -- > UserService { > 1: List<User> getUser(1: i32 id) > } > where an empty list is returned of no user is found, and a list containing > one item is returned if a user is found. > -- or -- > UserResult { > 1: bool exists; > 2: User user; > } > UserService { > 1: UserResult getUser(1: i32 id) > } > I don't like any of them. > One could solve this by letting the "optional" keyword apply to methods: > UserService { > 1: optional User getUser(1: i32 id) > } > or adding a new type to the IDL; the Option type: > UserService { > 1: Option<User> getUser(1: i32 id) > } > and then, like Scrooge does, generate code that wraps the optional fields in > an "Option" type, which is null-safe. > The Option type could also be used in structs: > struct User { > 1: i32 id; > 2: string name; > 3: optional string email; > } > -- or -- > struct User { > 1: i32 id; > 2: string name; > 3: Option<string> email; > } > Either way, a null-safe Option wrapper would be used in the generated java > code. -- This message is automatically generated by JIRA. If you think it was sent incorrectly, please contact your JIRA administrators For more information on JIRA, see: http://www.atlassian.com/software/jira