Использование Either в поиске, где в Haskell нет ключа - PullRequest
0 голосов
/ 28 декабря 2018

Я следую за этим:

https://www.schoolofhaskell.com/school/starting-with-haskell/basics-of-haskell/10_Error_Handling#either-may-be-better-than-maybe

И я пытаюсь получить столбец данных из CSV по имени переменной в заголовках, заданная ранее часть здесь .

Вот мой код:

import Text.CSV
import Data.List
import Data.Maybe

dat <- parseCSVFromFile "/home/user/data.csv"
headers = head dat
records = tail dat

indexField :: [[Field]] -> Int -> [Field]
indexField records index = map (\x -> x !! index) records

, который работает:

Prelude> indexField records 0
[1,2,3]

И заголовки следующие:

Prelude> headers
["id", "category", "value"]

У меня есть следующее для индексации по полю имя вместо index

indexFieldbyName :: [[Field]] -> String -> [Field]
indexFieldbyName records indexName = indexField records (fromJust (elemIndex indexName headers))

Что также работает:

Prelude> indexFieldbyName records "id"
[1,2,3]

Однако, Я хочу, чтобы эта ошибка более информативна, когда ключ не найден в headers:

Prelude> indexFieldbyName records "meow"
Maybe.fromJust: Nothing

Вот мои попытки:

indexFieldbyName2 :: [[Field]] -> String -> Either String [Field]
indexFieldbyName2 records indexName = indexField records index
  where index = case (elemIndex indexName headers) of
    Just v -> Right (v)
    Nothing -> Left ("Index not found")

Parse error (line 31, column 5): parse error on input ‘Just’

И

indexFieldbyName3 :: [[Field]] -> String -> Either String [Field]
indexFieldbyName3 records indexName = indexField records index
 where index = case v of
   Just (elemIndex indexName headers) -> Right (v)
   Nothing -> Left ("Index not found")

Parse error (line 44, column 4): parse error on input ‘Just’

indexFieldbyName4 :: [[Field]] -> String -> Either String [Field]
indexFieldbyName4 records indexName = indexField records index
 where index = case v of
   Just v -> Right (elemIndex indexName headers)
   Nothing -> Left ("Index not found")

Parse error (line 37, column 4): parse error on input ‘Just’

Выше были проблемы с отступами, just должен был быть справа от case.Теперь у меня есть:

indexFieldbyName2' :: [[Field]] -> String -> Either String [Field]
indexFieldbyName2' records indexName = indexField records index
    where index = case (elemIndex indexName headers) of
                     Just v -> Right (v)
                     Nothing -> Left ("Index not found")

<interactive>:4:39: error:
    • Couldn't match expected type ‘Either String [Field]’ with actual type ‘[Field]’
    • In the expression: indexField records index
      In an equation for ‘indexFieldbyName2’:
          indexFieldbyName2 records indexName
            = indexField records index
            where
                index
                  = case (elemIndex indexName headers) of
                      Just v -> Right (v)
                      Nothing -> Left ("Index not found")
<interactive>:4:58: error:
    • Couldn't match expected type ‘Int’ with actual type ‘Either String Int’
    • In the second argument of ‘indexField’, namely ‘index’
      In the expression: indexField records index
      In an equation for ‘indexFieldbyName2’:
          indexFieldbyName2 records indexName
            = indexField records index
            where
                index
                  = case (elemIndex indexName headers) of
                      Just v -> Right (v)
                      Nothing -> Left ("Index not found")

Ответы [ 2 ]

0 голосов
/ 28 декабря 2018

Ну, решение произошло в обсуждении.

Оказалось, что было две проблемы:

Первый отступ был неверным.Есть немного прочитанного в haskell wikibook .Короче говоря, когда код находится в той же строке, что и where, следующие строки должны начинаться с того же символа, что и первое слово после символа where.Но так как Just / Nothing относится к случаю в предыдущей строке, необходим другой уровень отступа.

Второй был обычным явлением при работе с функторами.Тип возвращаемого значения был уже не Int, а f Int.Так что indexField records index нужно переписать на fmap (indexField records) index.Или, поскольку это обычный шаблон, он получил оператор <$>, то есть (indexField records) <$> index.(Подумайте, что вы могли бы также написать оригинальное утверждение как (indexField records) $ index).

0 голосов
/ 28 декабря 2018

Для очистки вашей функции у вас есть:

indexFieldbyName2' :: [[Field]] -> String -> Either String [Field]
indexFieldbyName2' records indexName = indexField records index
    where index = case elemIndex indexName headers of
                     Just v -> Right v
                     Nothing -> Left "Index not found"

Тип index - это Either String Int, а indexField records требует Int.Вы можете написать еще один case или использовать fmap, но вы можете найти код более читабельным, прямолинейным:

indexFieldbyName3 :: [[Field]] -> String -> Either String [Field]
indexFieldbyName3 records indexName =
    case elemIndex indexName headers of
       Just v  -> Right (indexField records v)
       Nothing -> Left "Index not found"

Или использовать больше вспомогательных функций, таких как maybe вместо case:

indexFieldbyName4 :: [[Field]] -> String -> Either String [Field]
indexFieldbyName4 records indexName =
    let midx = elemIndex indexName headers
        err  = Left "Index not found"
    in maybe err (Right . indexField records) midx

NB. Ответ набран, не проверен.

...