23

I'm still new to Haskell (learning it on and off). I'm wondering why Haskell doesn't have a literal Data.Map constructor syntax, like the Map/Hash constructor syntax in Clojure or Ruby. Is there a reason? I thought that since Haskell does have a literal constructor syntax for Data.List, there should be one for Data.Map.

This question is not meant to be critical at all. I would just like to learn more about Haskell through the answers.

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
dan
  • 43,914
  • 47
  • 153
  • 254
  • Please let me know if this is an unconstructive question. – dan May 28 '12 at 14:24
  • 13
    It *sort of* has one in that you can write `fromList [(1,"foo"),(2,"bar")]` to get a map that sends `1` to `"foo"` and `2` to `"bar"`. I know that this isn't actually a literal, but it's pretty close. It is a constant applicative form, so the function call can be optimized away by the compiler. – Chris Taylor May 28 '12 at 14:28
  • Yes, I agree that's not really cumbersome at all. – dan May 28 '12 at 14:29
  • 5
    It's the wrong question. The right question is why do lists have special syntax? :) (The answer is: it's a historical accident.) – augustss May 29 '12 at 06:53
  • Why you need special syntax for Map? – 0xAX May 29 '12 at 10:54

5 Answers5

18

Unlike Clojure and Ruby, Haskell's finite maps are provided as libraries. This has tradeoffs: for example, as you noticed, there's no built-in syntax for finite maps; however, because it's a library, we can (and do) have many alternative implementations, and you as a programmer can choose the one that's most appropriate for your uses.

Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
  • 2
    On the other hand, we have the `OverloadedStrings` extension, why shouldn't there be a `OverloadedLists` extension as well? :-) – hvr May 29 '12 at 07:55
  • Would it be possible to create something with template Haskell that sort of gives you a Map literal? I'm not that familiar with how Haskell macros work so forgive me if that's a dumb question. – Wes Dec 13 '12 at 20:04
  • @Wes: pretty sure you can do that with TH — heck, you can even do things like SQL with TH. – Erik Kaplun Jan 05 '16 at 13:14
  • @DanielWagner what is wrong with OverloadedStrings? It seems like a really cool idea, and seeing as numbers are overloaded and `[Char]` is often not the best way to represent text, surely it is useful? Would you say Haskell should convert numbers to `5`, `50L`, `5.0` and `5.0F`. This is not meant to be a jab I am genuinely curious, I am not experienced enough with Haskell to know all the downsides of such things. – semicolon Mar 07 '16 at 17:49
  • 1
    @semicolon My comment was a bit tongue-in-cheek. However, there is some truth to it as well: `OverloadedStrings` hides a call to `fromString` in a way that makes it easy to forget it's there, which can cause surprising runtimes and surprising behavior; also, because of the simplistic type of `fromString` it is very tempting to write instances that do bad things like choose a terrible encoding or do some parsing when you aren't expecting it, and this temptation can also cause subtle bugs. Generally: "explicit is better". – Daniel Wagner Mar 08 '16 at 08:30
  • @DanielWagner Ah ok; is the reason why it is not as dangerous to use overloaded numbers because `fromInteger` is much less dangerous than `fromString`? Also why can't one overloaded literal be used twice with a different type each time. `x = 5` `y = x / 5` `z = div x 5` doesn't compile. – semicolon Mar 08 '16 at 17:41
  • 1
    @semicolon `fromInteger` actually shares *some* of the same problems as `fromString`; however, for some reason, extant libraries have mostly managed to avoid the temptation to do weird things in `fromInteger`. I admit I have no technical explanation for this outcome. For your second question, you may like to read [What is the monomorphism restriction?](http://stackoverflow.com/q/32496864/791604). – Daniel Wagner Mar 08 '16 at 18:59
  • @DanielWagner thanks for the help! If library designers avoid doing anything crazy are things like OverloadedLists and OverloadedStrings worth using? As they make it easier to write more generic code, and more generic pattern matching. – semicolon Mar 08 '16 at 21:14
13

In addition to the answers already given (the "historical accident" one notwithstanding), I think there's also something to be said for the use of Data.Map in Haskell compared to Hash in Ruby or similar things; map-like objects in other languages tend to see a lot more use for general ad-hoc storage.

Whereas in Haskell you'd whip up a data definition in no time at all, creating a class in other languages tends to be somewhat heavy-weight, and so we find that even for data with a well-known structure, we'll just use a Hash or dict or similar. The fact that we have a direct syntax for doing so makes it all the more attractive an option.

Contrast to Lisp: using MAKE-HASH-TABLE and then repeatedly SETFing it is relatively annoying (similar to using Data.Map), so everything gets thrown into nested lists, instead—because it's what's convenient.

Similarly, I'm happy that the most convenient choice for storing data is creating new types to suit, and then I leave Data.Map to when I'm actually constructing a map or hash-table as an intrinsic component. There are a few cases where I think the syntax would be nice (usually just for smaller throw-away programs), but in general I don't miss it.

Asherah
  • 18,948
  • 5
  • 53
  • 72
9

Actually I'm not sure why nobody has pointed it out in an answer (there's only sam boosalis' comment) but with OverloadedLists you can pretty much get literal syntax for Map and Set:

{-# LANGUAGE OverloadedLists #-}

import Data.Map
import Data.Set

foo :: Map Int Int
foo = [(1,2)]

bar :: Set Int
bar = [1]

from there it's only one more step to get even nicer looking maps, for example:

a =: b = (a,b)

ages :: Map String Int
ages = [ "erik"  =: 30
       , "john"  =: 45
       , "peter" =: 21 ]

Although I personally prefer explicit over implicit, so unless I'm building a DSL, I'd still stick to fromList and (foo, bar) — Haskell is about the big wins not the small ones.

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
7

Haskell has special syntax for lists because in a lazy functional language they more or less take the place of loop control structures in imperative languages. So they're much more important than Map in the grand scheme.

Also, I know you were referring to [1,2,3] when you said "list syntax", but I wanted to add that list constructor syntax could almost be implemented in haskell-98, in that type constructors can be infix when they start with :, e.g.

data Pair = Int :-- Int

So the list constructor : is just a slight special case of this general syntax rule, which is pretty elegant. Some people miss that.

jberryman
  • 16,334
  • 5
  • 42
  • 83
  • 1
    ML (strict) also has special syntax for lists. Special privileges for lists are part of functional programming's history rather than deference to the lazy evaluation. – stephen tetley May 28 '12 at 17:22
  • 4
    @stephentetley good point, thanks. But I think lazy evaluation is necessary for lists to replace loops (e.g. `while true; do`) and I think that's the fundamental thing about lists in haskell. – jberryman May 28 '12 at 18:07
0

Haskell does have a Map constructor, but it is "hidden" (like a private method in an object oriented paradigm). You are encouraged to use "public" constructors, such as empty, singleton or fromList. However, if you inspect the code, available at https://hackage.haskell.org/package/containers-0.4.0.0/docs/src/Data-Map.html , you get the following definition

data Map k a  = Tip
              | Bin {-# UNPACK #-} !Size !k a !(Map k a) !(Map k a)

You can use the Tip and Bin constructors, but that is not recommended.

  • I don't think you can use the constructors, as `Map(..)` are hidden behind a `#if defined(TESTING)` (https://hackage.haskell.org/package/containers-0.5.6.3/docs/src/Data-Map-Lazy.html) – sam boosalis Jul 24 '15 at 22:33
  • 3
    Even if these constructors were "public", they would still have nothing to do with a hypothetical map-literal syntax — lists too have the constructors `[]` and `:`, and you are free to use them, but the literal syntax is sugar on top of that. – Erik Kaplun Jan 05 '16 at 13:12