Hi everyone - i am writing a parse function on a C...
# announcements
w
Hi everyone - i am writing a parse function on a Command model that I am taking (can be thought of as modeling any prompt based command). So this command is made up of arguments, e.g. an arugment for the
ping
command would be
-n 3
where
-n
is the name and
3
is the value. One of the things I am stuck on is writing a parse function on the input that will output a type based on what the argument type. So in this example for
-n 3
we would want the value returned to be
3
which is an
Int
. In some other cases we would want to parse as a string, boolean, etc. I am not sure how to make this generic to allow for any return types. I thought about doing something like
fun <T> parse(text: String, conversion: (String) -> T): T
where I could pass in a conversion function to turn a string into whatever value I wanted but I don't know how to supply a default value to that e.g. if none is provided just return it as a string. Thoughts?
n
I have a system exactly like this. I throw an exception if an unsupported type is requested. Meaning that if a nullable type is requested, and can be parsed,
null
is a valid return. In terms of a default case you should probably not have one, except for primitives
w
do you mean that you implement a default group of types, and as an input you allow the user to choose what they want? In my example I would implement something that parses into a String, Int, Boolean, etc and let the method caller choose and throw an Unsuported exception if something else is given? @nwh
n
So there are two layers to my system. The bottom one is basically a type parser, where strings can become requested types, eg:
Copy code
types.produce<Int>("123") // returns 123
Then, for the flag system that you're describing, I use data classes to represent the arguments. By using the default constructor and the parameters it contains you get the type that you can pass the bottom layer, and the name of the argument
w
could you provide an example of what your produce function would look like?
n
Sure. Do you want psuedo-code for a learning experience or should I show you it?
w
psuedo code is fine i am struggling to figure out the signature
n
Well, I separate the classes into "producers" and then the system itself, which has all the producers. Each producer is responsible for a single type. So, when a type is requested, I check all the registered producers to make sure the type is supported. If it is, use that parser, otherwise throw an exception that the type is unsupported
w
ok but I see you specify the type in your call
Copy code
types.produce<Int>("123")
- im just not sure how to access the type from the method?
n
Oh, right. The method there is
inline
and uses a
reified
type. I have another method that's basically
types.produce(Int::class, "123")
, which it calls. I'll show you the signature for the reified one though
Copy code
inline fun <reified T : Any?> produce(input: String): T? = produce(T::class.java, input)
Your other option is to just use a signature like this:
Copy code
fun <T : Any?> produce(type: Class<T>, input: String): T?
Using the inline/reified trick is a kotlin specific advantage, it's not much different for this use case
w
hm interesting. I am not familiar with
reified
so I will look into that. The second example definately makes sense though i will give it a try. thank you
👍🏻 1
with your second example i can use a
when
on the type argument passed in however when I try and return I am getting
Type mismatch. Expected T, Given String
or whatever I am trying to return based off of the type passed in. Not sure how to get the return to conform to
T
n
Why would you use
when
instead of
<T : Any?>
? I'm not familiar with
when
w
when is like a switch statement in java
n
Whoops, I thought you meant
where
- it can be used to constrain generic parameters. Can you show the statement?
w
Copy code
fun <T : Any> parse(input: String, type: KClass<T>): T {
        when (type) {
            String::class -> {
                return ""
            }
            ...
            else -> {
                throw UnsupportedOperationException()
            }
        }
    }
on the return "" I get the type mismatch
which makes sense - but i am not sure how to avoid it
n
Copy code
when (T) {
   is String -> return ""
}
Nevermind, this won't work for non-reified types. I think you just have to do an unsafe cast on it
t
I don't have a better solution than what's already proposed but if you just want a solution that works so you can move on, it's a solved problem -- see http://commons.apache.org/proper/commons-cli/ for one example.
w
@nwh since T is not an expression I cannot use a when on it
its just a type parameter
I can do
Copy code
fun <T : Any> parse(input: String, type: KClass<T>): T {
        when (type) {
            String::class -> return "..." as T
            Int::class -> return 0 as T
            else -> {
                throw UnsupportedOperationException()
            }
        }
    }
but then i get a warning on casting to
as T
without checking
n
Yeah, I don't think you can avoid that warning
w
hmm ok ill go with it for now and see if i can figure anything better out
thanks for the help
n
No worries