Как работать со списком «Может быть [значения]» в Haskell?

Я попытался адаптировать некоторые упражнения из прекрасной книги Land of Lisp Конрада Барски, чтобы выучить Haskell. Идея состоит в том, чтобы сделать простой текстовый игровой движок.

Конкретно я пробовал:

type Clau = String
type Descripcio = String
type Valors = [String]

-- NOTE : Ideas of types http://learnyouahaskell.com/making-our-own-types-and-typeclasses
data Lloc = Lloc String String String deriving (Show) 
llocSituacio :: Lloc -> String  
llocSituacio (Lloc situacio _ _ ) = situacio  
llocDireccio :: Lloc -> String
llocDireccio (Lloc _ direccio _) = direccio
llocPas :: Lloc -> String
llocPas ( Lloc _ _ pas) = pas

nodes :: [(Clau,Descripcio)]
nodes = [("living-room","you are in the living-room. a wizard is snoring loudly on the couch.")
           ,("garden","you are in a beautiful garden. there is a well in front of you.")
           , ("attic", "you are in the attic. there is a giant welding torch in the corner.")]

edges :: [([Char], [Lloc])]
edges = [ ("living-room", [(Lloc "garden"  "west" "door"), ( Lloc "attic" "upstairs" "ladder") ])
        , ("attic", [(Lloc "living-room"  "east"  "door")])
        , ("garden", [(Lloc "living-room" "east"  "door")])]

describePath :: Lloc -> String  
describePath  e = "There is " ++ llocPas e ++ " going " ++ llocDireccio e ++ " from here." 

Сначала кажется, что работает хорошо. Например:

*TextGame> describePath (Lloc "living-room"  "east"  "door")
"There is door going east from here."

Но когда я пытаюсь применить функцию к списку, я получаю эту ошибку:

situacio = "garden"
map (describePath) (lookup situacio edges)



<interactive>:2:22: error:
    • Couldn't match expected **type ‘[Maybe Lloc]’**
                  with actual **type ‘Maybe [Lloc]’**
    • In the second argument of ‘map’, namely ‘(lookup situacio edges)’
      In the expression: map (describePath) (lookup situacio edges)
      In an equation for ‘it’:
          it = map (describePath) (lookup situacio edges)

Ошибка ясна, но мне не удается ее решить. Я хочу проанализировать список значений Maybe и напечатать путь с помощью функции descriptionPath, которая хорошо работает:

Любые идеи? Кроме того, если кто-то хочет поделиться альтернативами или считает, что этот код мог бы быть больше в стиле Haskell, пожалуйста, не стесняйтесь говорить об этом.


person Mandorman    schedule 01.06.2021    source источник
comment
Возможно, этот старый ответ полезен? stackoverflow.com/questions/3375483/   -  person Thomas M. DuBuisson    schedule 03.06.2021
comment
это map-function, а не карта.   -  person Will Ness    schedule 03.06.2021


Ответы (2)


Могут быть более продвинутые вспомогательные библиотеки, но я думаю, что вы должны сначала научиться обрабатывать Maybe самым простым (и общим) способом: использовать сопоставление с образцом.

case lookup situacio edges of
   Nothing   -> [] -- not found, how do you want to handle this case?
   Just locs -> map describePath locs

Часто вместо этого хочется перевернуть результат в другой Maybe, например:

case lookup situacio edges of
   Nothing   -> Nothing -- not found
   Just locs -> Just (map describePath locs)

и в таком случае мы можем использовать вспомогательную функцию библиотеки, чтобы сократить код:

map describePath <$> lookup situacio edges
person chi    schedule 01.06.2021
comment
Большое спасибо за ваш ответ! Он работает отлично! Но я понял, что не до конца понимаю оператор ‹$›, он отличается от fmap да!? Он включает в себя шаблон, который вы указали!? Это точно разница?! - person Mandorman; 02.06.2021
comment
@Mandorman f <$> x точно fmap f x для функтора Maybe: он применяет f под Just и оставляет Nothing как есть. - person chi; 02.06.2021

Большое спасибо, чи, все работает отлично. Я только что сделал новую функцию:

describePaths situacio edges = case lookup situacio edges of
    Nothing -> []
    Just locs -> map describePath locs

.. и работает очень хорошо:

*TextGame> situacio = "living-room"
*TextGame> describePaths situacio edges
["There is door going west from here.","There is ladder going upstairs from here."]

Но я вижу, что не до конца понимаю оператор ‹$›. Я следую комментариям: Что означает ‹$› в Haskell?

Поэтому я следую предложению: https://hoogle.haskell.org/?hoogle=%3C$%3E

Это вызов Functor:

(<$>) :: Functor f => (a->b) -> f a -> f b

На самом деле это то же самое, что и fmap:

fmap :: Functor f => (a -> b) -> f a -> f b

Вроде одинаковые:

*TextGame> (*2) <$> [1..3]
[2,4,6]
*TextGame> fmap (*2) [1..3]
[2,4,6]

Но на самом деле они разные:

*TextGame> map describePath <$> lookup situacio edges
Just ["There is door going west from here.","There is ladder going upstairs from here."]
*TextGame> fmap (describePath) (lookup situacio edges)

<interactive>:30:22: error:
    • Couldn't match type ‘[Lloc]’ with ‘Lloc’
      Expected type: Maybe Lloc
        Actual type: Maybe [Lloc]
        ....

Может ли кто-нибудь пролить на это немного больше света? Я не совсем понимаю, почему «fmap (describePath) (lookup situacio edge)» не работает? (Я играю с Maybe's и Just's, но...)

person Mandorman    schedule 02.06.2021
comment
Вы удалили map из последнего примера, правильный перевод fmap (map describePath) (lookup situacio edges). fmap здесь обрабатывает Maybe, map обрабатывает список в [Lloc]. - person chi; 02.06.2021
comment
Ага, понятно! Я уловил идею сейчас!. Большое тебе спасибо! - person Mandorman; 02.06.2021