Scala Map, implict symbol key to string - string

UPDATE
All the answers are good here, but #senia's does so the most directly, without need for additional steps. Will this lead to bugs, possibly, but when using Map[Symbol, T] convention in hundreds of methods, a 1-step implicit conversion prior to map creation is preferred (avoids Symbol Map key permgen storage). At any rate, here's the pimp:
class SymbolProvidesPair(i: Symbol) { def ->[T](s: T) = (i.toString.tail, s) }
#inline implicit def symbol2String(i: Symbol) = new SymbolProvidesPair(i)
Original
It bothers me a bit using string keys in Maps, just slows me down and is, IMO, not as syntactically easy on the eyes as symbol keys.
val map: Map[String, Int] = Map("strings" -> 1, "blow" -> 2)
val map: Map[String, Int] = Map('symbols -> 1, 'rock -> 2)
So, I created an implicit to scratch my itch:
implicit def symbolKey2String[A <: Symbol, B](x:(A,B)) = (x._1.toString, x._2)
Couple things:
1) is this the correct signature? The above works, but A <: Symbol I take to mean, something that derives from Symbol vs. something that equals Symbol.
2) I'll be using this when I manually type out Maps; i.e. just for convenience. Am I going to hit any snags with this implicit? It seems edge case enough to not cause issues (like string2Int, for example), but not sure if I'm missing something.
Thanks
EDIT
Ok, well #1 I can just actually say what I mean, [Symbol, B] instead of [A <: Symbol, B]
But now I find myself with another issue, the symbol-to-string implicit boxes me into a corner of sorts as I then have to explicitly define Map[String, Type] for all new Maps (i.e. lose the nice compiler type inference) in order to be able to use symbol keys.
How then to get the best of both worlds, Map symbol keys, but with inferred [String, Type] when not specifying the type signature? i.e. have the compiler infer Map[String, Int] when I do:
val map = Map('foo -> 1)

You don't need to specify map's type explicitly:
scala> class SymbolToPait(i: Symbol) { def ->[T](s: T) = (i.toString().tail, s)}
defined class SymbolToPait
scala> implicit def symbolToPair(i: Symbol) = new SymbolToPait(i)
symbolToPair: (i: Symbol)SymbolToPait
scala> 'Symbol -> "String"
res0: (String, String) = (Symbol,String)
scala> Map('Symbol -> "String")
res1: scala.collection.immutable.Map[String,String] = Map(Symbol -> String)
scala> Map('Symbol -> 1)
res2: scala.collection.immutable.Map[String,Int] = Map(Symbol -> 1)
This kind of behavior could surprise other developers. Maybe it would be better to replace -> with some other word? For example :-> or ~>.

As you noted, there is no need for A. You probably want to drop the first character as well, which is always a '
implicit def symbolKeyToString[B](x: (Symbol, B)) = (x._1.toString.tail, x._2)
As for snags, well, you have to type out the signature of the map every time, and your keys can't contain spaces or operator characters. This is not something I would do myself...
Edit: if you don't want to type out the signature each time, use an alternative to Map.apply and forget implicits:
object Map2 {
def apply[B](xs: (Symbol, B)*) =
xs map {case (k, v) => (k.toString.tail, v)} toMap
}

I have a couple of warnings about the current solutions.
First of all, you're changing the meaning of 'sym -> x, and it will mean something different from ('sym, x). I would find this confusing.
You also make it difficult to mix code that uses this conversion with code that actually needs Map[Symbol, _].
Instead of converting the symbols to strings before putting them into a map, I recommend just converting the map. Seems much more straightforward to me.
scala> implicit def symMap2strMap[T](m: Map[Symbol, T]): Map[String, T] = m.map {
| case (key, value) => key.toString.tail -> value
| }
symMap2strMap: [T](m: Map[Symbol,T])scala.collection.immutable.Map[String,T]
scala> val sym = Map('foo -> 1, 'bar -> 2)
sym: scala.collection.immutable.Map[Symbol,Int] = Map('foo -> 1, 'bar -> 2)
scala> sym: Map[String, Int]
res0: Map[String,Int] = Map(foo -> 1, bar -> 2)
Edit:
You should never have to specify the type to explicitly convert Map[Symbol, T] to Map[String, T]. Just leave it as a Map[Symbol, T] until you hit an API which requires string keys, then let Scala implicitly convert it to the type you want.

Related

Haskell - Using one data type kind in another

Haskell newbie; I want to be able to declare Val which can be either IntVal, StringVal FloatVal and a List which can be either StringList, IntList, FloatList, whose elements are (correspondingly): StringVal, IntVal and FloatVal.
My attempt so far:
data Val = IntVal Int
| FloatVal Float
| StringVal String deriving Show
data List = IntList [(IntVal Int)]
| FloatList [(FloatVal Float)]
| StringList [(StringVal String)] deriving Show
fails with the error:
Not in scope: type constructor or class ‘IntVal’
A data constructor of that name is in scope; did you mean DataKinds?
data List = IntList [(IntVal Int)]
... (similarly for StringVal, FloatVal..)
what is the right way to achieve this?
PS:
declaring List as data List = List [Val] ends up allowing Lists as follows:
l = [(IntVal 10),(StringVal "Hello")], which I do not want to allow.
I want each element of list to be a Value of same kind
There is a solution using GADTs. The problem is that IntVal etc are not actually types, they are just constructors (basically functions that also support pattern matching) for the single type Val. So once you have made a Val, the information about which kind of value it is is completely lost at the type level (that is, compile time).
The trick is to tag Val with the type it contains.
data Val a where
IntVal :: Int -> Val Int
FloatVal :: Float -> Val Float
StringVal :: String -> Val String
Then if you have a plain list [Val a] it will already be homogeneous. If you must:
data List = IntList [Val Int]
| FloatList [Val Float]
...
which is slightly different in that it "erases" the type of list, and it can distinguish between an empty list of ints and an empty list of floats, for example. You could also use the same GADT trick with List
data List a where
IntList :: [Val Int] -> List Int
FloatList :: [Val Float] -> List Float
...
but in that case I think a better design is probably the simpler
newtype List a = List [Val a]
The trade-offs between all these different designs really depends on what you are planning to do with them.

Convert to typed value from String

I have file with list of values and types, after reading them I need to put them into db. For that, I need to supply insertion function with properly typed tuple, so I'm trying to convert values with something like this
toProperType :: String -> String -> a
toProperType tp val =
case tp of
"string" -> val -- ::String
"int" -> toIntType val -- ::Int64
"bigint" -> toIntType val -- ::Int64
"integer"-> toIntType val
"utcdate"-> toDateType val -- :: UTCTime
"double" -> toDoubleType val -- :: Double
Which is failing with
Couldn't match expected type ‘a’ with actual type ‘Double’
‘a’ is a rigid type variable bound by
which I think is correct.
What is proper way to achieve this functionality?
Maybe I need some extension or generate separate functions with TH(but not sure how to dispatch them)
The issue here is the meaning of -> a in your function type. If you're function actually had this type, then whoever called your function should be able to specify a concrete type of their choosing (that you may not even have in scope) and then expect your function to work as if it had the type
String -> String -> MyCustomType
However this clearly isn't what you had in mind. You don't mean "for all types a, I have a function ...", you mean "For any two strings, there is some type a for which I have a value". This idea, that you get to choose the type variable instead of the caller, is called "existential quantification" and GHC does support it. However I don't really think that's what you want to do. After all, when you go to actually use this function, you'll probably want to be able to case on whether or not you got back a UTCTime or a Double or something. Since you cannot do this with existential quantification (just like how you cannot case on type variables in polymoprhic functions) we should instead create a custom data type:
data Dyn = String String | Int Int | BigInt Integer | UTCDate UTCTime ...
and so on. That is, you list out an explicit constructor for each case that your type may return and then your function will read
toProperType :: String -> String -> Dyn
toProperType tp val =
case tp of
"string" -> String val -- ::String
"int" -> Int $ toIntType val -- ::Int64
"bigint" -> BigInt $ toIntType val -- ::Int64
"integer"-> Integer $ toIntType val
"utcdate"-> UTCDate $ toDateType val -- :: UTCTime
"double" -> Double $ toDoubleType val -- :: Double
This is how serious Haskell libraries handle things like JSON parsing or what not so you're in good company. Now it's well typed and whoever calls this function just cases on the Dyn value and decides what to do based on the returned type.

Create a type that is a subset of an other type

How can you create a type that is a subset of an other type? I want a string type that only contains alphanumeric characters.
So I want something like this
type AlphNumString = [AlphaNumChar]
data AlphaNumChar = ???? filter (isAlphaNum) Char ????
The standard way to do this is with so-called "smart constructors".
First, you define a new type that's identical to the old one:
newtype AlphNumString = X String
Next, you write the smart constructor itself:
toAlphNumString :: String -> AlphNumString
toAlphNumString txt = X (filter isAlphNum txt)
Finally, you make it so toAlphNumString is the only way to create an AlphNumString.
module Foo (AlphNumString (), toAlphNumString, ...) where ...
Note that this does not allow you to use an AlphNumString like a normal String; you can't create "subtypes" like that in Haskell. So you'll also need another function
fromAlphNumString :: AlphNumString -> String
fromAlphNumString (X txt) = txt
This concept of types that are "subsets" of other types based on some predicate is called refinement types. For Haskell, this is implemented as LiquidHaskell.
However, I would consider this an ongoing research. In practice, I would go with a newtype and dynamic checks, as MathematicalOrchid describes in their answer.
In a dependently typed language (which Haskell is not, yet) you could use dependent pairs for this.
data Sigma : (t : Type) -> (t -> Type) -> Type where
MkSigma : (x : t) -> p x -> Sigma t p
data IsAlphaNum : (c : Char) -> Type where
MkIsAlphaNum : isAlphaNum c = True -> IsAlphaNum c
Then Sigma Char IsAlphNum would be a type representing alphanumeric characters. Each element of that type would consist of a character and a proof that the character is alphanumeric.

How to use Data.Map.lookup in the context of an Either monad?

I'm trying to use Data.Map to map strings to functions. The issue I'm having is that the main error handling in my program uses an Either monad, and Map.lookup will return Maybe ([SomeVal] -> Either ValError SomeVal). How can I make Map.lookup play nicely with the Either monad in this case?
apply :: String -> [SomeVal] -> Either ValError SomeVal
apply s args = (Map.lookup s prims) >>= \fn -> fn args
prims :: Map String ([SomeVal] -> Either ValError SomeVal)
prims = Map.fromList
[("key", function)
,("key2", function2)
]
::> apply "key" [val1, val2, val3]
The Control.Error package has a nice function
note :: e -> Maybe a -> Either e a
note e Nothing = Left e
note _ (Just a) = Right a
which is useful for "upgrading" Maybe-like failures to Either-like ones.
lookupNote :: k -> Map.Map k v -> Either ValError v
lookupNote k = note (Missing k) . lookup k
It also has many others like this for mapping between transformers of Either and Maybe and generic MonadPlus instances. I highly recommend that package.
The other answers have explained how to turn Maybe into Either in general. However for Data.Map specifically, instead of Map.lookup, you can use Map.findWithDefault, which doesn't wrap in a Maybe and allows you to choose what to do with non-existing keys.
That depends on how you want to handle the Nothing case. If you want to assert that the key will always be there (a bad idea)
import Data.Maybe
apply s args = (prims ! s) args
But the smarter method is to have a sane value in the case where nothing is found. Likely you'll want some ValError constructor
ValError = ...
| NoKey String
...
apply s args = maybe (Left $ NoKey s) ($args) $ M.lookup s prims
Or want to just keep the Maybe
apply :: String -> [SomeVal] -> Maybe (Either ValError SomeVal)
Either of these methods provide much saner semantics in the case where the key wasn't found. Which you choose is mostly preference, if you intend to make apply a pretty fundamental part of your API so much so that ValErrors should know about it, then 1 works great.
Otherwise, 2 is more painful to use, but doesn't require changing any existing code which is pleasant.

Can I statically reject different instantiations of an existential type?

First attempt
It's difficult to make this question pithy, but to provide a minimal example, suppose I have this type:
{-# LANGUAGE GADTs #-}
data Val where
Val :: Eq a => a -> Val
This type lets me happily construct the following heterogeneous-looking list:
l = [Val 5, Val True, Val "Hello!"]
But, alas, when I write down an Eq instance, things go wrong:
instance Eq Val where
(Val x) == (Val y) = x == y -- type error
Ah, so we Could not deduce (a1 ~ a). Quite right; there's nothing in the definition that says x and y must be the same type. In fact, the whole point was to allow the possibility that they differ.
Second attempt
Let's bring Data.Typeable into the mix, and only try comparing the two if they happen to be the same type:
data Val2 where
Val2 :: (Eq a, Typeable a) => a -> Val2
instance Eq Val2 where
(Val2 x) == (Val2 y) = fromMaybe False $ (==) x <$> cast y
This is pretty nice. If x and y are the same type, it uses the underlying Eq instance. If they differ, it just returns False. However, this check is delayed until runtime, allowing nonsense = Val2 True == Val2 "Hello" to typecheck without complaint.
Question
I realize I'm flirting with dependent types here, but is it possible for the Haskell type system to statically reject something like the above nonsense, while allowing something like sensible = Val2 True == Val2 False to hand back False at runtime?
The more I work with this problem, the more it seems I need to adopt some of the techniques of HList to implement the operations I need as type-level functions. However, I am relatively new to using existentials and GADTs, and I am curious to know whether there's a solution to be found just with these. So, if the answer is no, I'd very much appreciate a discussion of exactly where this problem hits the limit of those features, as well as a nudge toward appropriate techniques, HList or otherwise.
In order to make type-checking decisions based on the contained types, we need to "remember" the contained type by exposing it as a type parameter.
data Val a where
Val :: Eq a => a -> Val a
Now Val Int and Val Bool are different types, so we can easily enforce that only same-type comparisons are allowed.
instance Eq (Val a) where
(Val x) == (Val y) = x == y
However, since Val Int and Val Bool are different types, we cannot mix them together in a list without an additional layer which "forgets" the contained type again.
data AnyVal where
AnyVal :: Val a -> AnyVal
-- For convenience
val :: Eq a => a -> AnyVal
val = AnyVal . Val
Now, we can write
[val 5, val True, val "Hello!"] :: [AnyVal]
It should hopefully be clear by now that you cannot meet both requirements with a single data type, as doing so would require both "forgetting" and "remembering" the contained type at the same time.
So you want a constructor that allows you to use heterogeneous types, but you want comparisons between heterogeneous types that are knowable at compile time to be rejected. As in:
Val True == Val "bar" --> type error
allSame [] = True
allSame (x:xs) = all (== x) xs
allSame [Val True, Val "bar"] --> False
But surely:
(x == y) = allSame [x,y]
So I'm pretty sure a function with satisfies these constraints would violate some desirable property of a type system. Doesn't it look like that to you? I am strongly guessing "no, you can't do that".

Resources