The value of an environment variable (in terms of the operating system) is a string, and you can't beat this, in particular not with a language which has a very unusualy idea of a null pointer (since nil is an object of a full-featured class in Ruby, much different to i.e. C or Java).
Usually (when seen from i.e. shell programming), we sometimes distinguish between an environment variable which is empty, and one which is undefined. This you can do in your Ruby too:
ENV.has_key?('V') # -> true if defined, false if undefined
ENV['V'] # -> trueish if defined, nil if undefined
If you know that an environment variable is defined, you can check whether it is empty, by
ENV['V'].empty?
Transfering the Ruby type system to a data model established by the whole operating system, is usually futile, however there is a way, if you can live with restrictions:
Assuming that you need the environment variables to transport Ruby data (nil, arrays of strings, or whatever) from one Ruby process to another Ruby process, you can of course serialize the data. Serialization means that you create a string representation of the data (which makes it suitable for being stored in an environment variable) and deserialize it in the receiveing process.
Ruby comes with 3 serialization methods: JSON, YAML and (the most general and flexible one) Marshal. With JSON and YAML, you have even a chance to deserialize in a different language. With Marshal, you have to use the same Ruby implementation on the sending and receiving end.
With all serialization methods, you indeed can represent a Ruby nil in an Environment variable, for instance:
ENV['V']=Marshal.dump(nil) # serializing
v=Marshal.load(ENV['V']) # deserializing