-
Notifications
You must be signed in to change notification settings - Fork 663
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Request: add list comprehensions #147
Comments
Haskell actually discourages the use of list comprehensions in their style guide. I think they are cool and exciting, but I have never found a use for them in real Haskell code. It is much nicer in Python, but I think that's because you can't easily do the functional version in the first place. This is not on the roadmap for now. |
Ok, that makes sense. |
Doesn't Haskell discourage them because there are a bunch of other, better ways to use them using Monads? |
See the reasoning in the style guide I linked. It does not mention using Monads instead. I cannot speak for the larger Haskell community, which perhaps tells people to favor do notation. Overall I feel that both list comprehensions and monads are overkill for the handfull of cases where they really do make things slightly shorter. |
I'm trying to understand then how to do something simple like the code below. It uses the applicative style. It creates a cartesian style join between the years and the months. Lacking this applicative style, I was thinking ok I'll do it as a list comprehension. Then I see that's not supported because a Haskell style guide frowns on it. I'm sure I'm lacking something conceptual here, but how do I do something as simple as a cartesian join between lists? monthlyDates dom = take 12 $ fromGregorian <$> [2015..] <> [1..12] <> pure dom |
Rewrite from Haskell to Elm: monthlyDates dom = take 12 $ fromGregorian <$> [2015..] <*> [1..12] <*> pure dom --remove infinite list--> monthlyDates dom = fromGregorian <$> [2015] <*> [1..12] <*> pure dom --apply monthlyDates dom = [fromGregorian 2015] <*> [1..12] <*> pure dom --singleton list = monthlyDates dom = fromGregorian 2015 <$> [1..12] <*> pure dom --applicative interchange law--> monthlyDates dom = ($ dom) <$> (fromGregorian 2015 <$> [1..12]) --I'm doing this step on intuition--> monthlyDates dom = (\m -> fromGregorian 2015 m dom) <$> [1..12] --replacing the general monthlyDates dom = map (\m -> fromGregorian 2015 m dom) [1..12] --valid Elm, and much more readable in my opinion--> monthlyDates dom = List.map (\m -> fromGregorian 2015 m dom) [1..12] Final note, if you want a cartesian join between lists, you can use andMap : List (a -> b) -> List a -> List b -- like (<*>) in Haskell, specialised to lists
andMap listOfFuncs listOfAs = List.concatMap (\f -> List.map f listOfAs) listOfFuncs
gridOf12x24 = List.map (,) [1..12] `andMap` [1..24] |
You committed a fairly major cheat in your steps. You turned my list of years into a single value. Given the day of month happens to also be a single value, the whole step by step section completely side stepped my question by focusing on producing a solution to the one example without actually attending to the conceptual question I asked. Now extend your example to do multiple years or start at a month other than January. I would like a solution that does so as an easy conceptual and syntactic step, as it would be in the Applicative style. I won't respond to readability since you didn't produce an equivalent solution. I understand you happen to have produced the equivalent list, but the construction methods leave me quite hamstrung to take next steps, like apply it starting at the current month and go for 12 months from there, for instance. Again, the conceptual question I was asking is how to do something as simple as combining two lists in a cartesian style. The standard answer I found for Haskell was list comprehensions. So now apparently lacking both list comprehensions and <*>, I was stuck as to how to do something like that, which to me is extremely basic. andMap though looks exactly like what I'm looking for. So thank you for that. That uses an applicative style. Without giving it much thought this late at night, I'm assuming this would leave it partially applied if instead of (,) you had used a ternary function for instance. I assume then you can just pass another list with another |
andMap is for Tasks, not for lists as you indicated. So I'm back to not knowing how to do join two list in a cartesian style, lacking both list comprehensions and Applicative List. |
|
Ah ok. My apologies. So concatMap is like SelectMany in Linq (I have a primarily C# background). That's really good to know too. And you used that to build an Applicative List implementation. Very nice. Thanks for your help with this. I really want to use Elm, but I'm getting hung up on some things I've learned in Haskell that seemed very nice and I'm trying to apply it in Elm. |
Using your andMap (calling it 'ap' for brevity, inspired by Ramda), I can now do the following, which is some very recognizable Applicative style. Thanks again. monthlyBills = List.map (,,) [2015..2020] |
Warning, this is kind of long. Another approach to arrive at a similar solution is to notice: fromGregorian <$> [2015..] <*> [1..12] <*> pure dom
== liftA3 fromGregorian [2015..] [1..12] (pure dom) And since N.B. This is not I feel this is instructive to help solidify the relationship between It's instructive to look at simpler examples first, and build up to
|
Thank you for taking the time to put that together @joneshf. Amazingly, I understood all that perfectly, with the only exception being the use of monomorphism. I've read the concepts before but it didn't quite connect there for me. |
Great! Glad it wasn't all mumbo. :) Sorry, that was supposed to read, "...slightly monomorphized...". Though maybe it would have been better to say "less polymorphic". I just meant that instead of: liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d Which works for any lift3 : (a -> b -> c -> d) -> List a -> List b -> List c -> List d |
The example at https://github.com/danfinch/fmarkup shows how much nicer Elm could be with list comprehensions imo. |
Nice is subjective. Personally I found that much less readable than what we have today. |
Maybe I'm doing elm wrong. I recently had do do this: ingredientView : Model -> Ingredient -> Html Msg
ingredientView model ingredient =
div []
[ text (prettyRound (model * ingredient.quantity))
, text " "
, text ingredient.unit
]
view : Model -> Html Msg
view model =
div []
[ div [] (List.map (ingredientView model) ingredients)
, button [ onClick Decrement ] [ text "-" ]
, button [ onClick Increment ] [ text "+" ]
] where really I wanted to do view : Model -> Html Msg
view model =
div []
[ div []
[ for ingredient in ingredients
div []
[ text (prettyRound (model * ingredient.quantity))
, text " "
, text ingredient.unit
]]
, button [ onClick Decrement ] [ text "-" ]
, button [ onClick Increment ] [ text "+" ]
] The currying is pretty ugly I think. In this case it's not horrible because I've written the function to be curried so I can make sure the parameters are in the right order, but if they needed to be the reverse order, I'd have to put in an anonymous function there. Compared to a list comprehension it's quite verbose, but it can easily become even worse. I'm open to suggestions for how to improve my thinking! |
@boxed You will get better results if you ask about this problem independent of new language features on elm-discuss, r/elm, or slack. |
Sure, but this is where people end up when googling so this is the closest thing you've got to documentation. So if I get an answer somewhere else, that just gets buried and the next person has to ask again, and the next, ad adfinitum.
|
I suggest that list comprehensions be added to Elm. They see heavy use in languages that implement them. Considering the existing similarities between Elm and Haskell syntax, I propose using Haskell style list comprehensions, like this:
The text was updated successfully, but these errors were encountered: