Поиск ключей (анализируемых как String) из записи Haskell - PullRequest
0 голосов
/ 05 мая 2020

Предположим, у меня есть запись Haskell, например

data HaskellRecord = HaskellRecord {
  _key1 :: Maybe String
, _key2 :: Maybe String
, _key3 :: Maybe String
}

Есть ли способ создать функцию

getKey :: HaskellRecord -> String -> Maybe String

так, чтобы с

let haskellRecord = HaskellRecord { _key1 = Just "value1"
                                  , _key2 = Just "value2"
                                  , _key3 = Just "value3"
                                  }

затем

getKey haskellRecord "value1" == Just "_key1"
getKey haskellRecord "value2" == Just "_key2"
getKey haskellRecord "value3" == Just "_key3"
getKey haskellRecord "value4" == Nothing

Ответы [ 3 ]

7 голосов
/ 05 мая 2020

Думаю, я бы написал примерно так:

getKey :: String -> HaskellRecord -> [String]
getKey needle haystack =
    [ name
    | (name, selector) <- [("_key1", _key1), ("_key2", _key2), ("_key3", _key3)]
    , selector haystack == Just needle
    ]

Посмотрите go:

> getKey "value1" haskellRecord
["_key1"]

... но я подозреваю, что это проблема XY. (Потому что тогда что вы собираетесь делать с этим String? Если ответ будет «превратить его обратно в селектор» или «использовать его для изменения соответствующего поля», то почему бы не вернуть линзу или что-то в этом роде? И если вы на самом деле хотите линзу, тогда вам, вероятно, вообще не нужна эта структура данных ...)

3 голосов
/ 05 мая 2020

Вы можете адаптировать мое решение для аналогичной проблемы . Он использует универсальные шаблоны scrap-your-template (SYB). Этот ответ довольно подробно объясняет код, поэтому я не буду его здесь объяснять. Ниже приведен пример кода, который работает с вашим примером. Он поддерживает типы данных со смесью полей Maybe String и String; другие типы полей игнорируются getKey.

Обратите внимание, что это не будет очень эффективным. Если это основная часть вашего приложения logi c, а не какой-то отладочный прием, вам следует подумать о переосмыслении ваших типов данных. Может быть, вам на самом деле нужен здесь не тип записи Haskell, а какая-то двунаправленная карта (например, пары Map k v и Map v k, доступ к которым осуществляется через набор функций, которые поддерживают их согласованность).

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

import Data.Tuple
import Data.Generics

data HaskellRecord = HaskellRecord {
  _key1 :: Maybe String
, _key2 :: Maybe String
, _key3 :: Maybe String
} deriving (Data)

-- Get field names (empty list if not record constructor)
getnames :: Data object => object -> [String]
getnames = constrFields . toConstr

-- Get field values as `Maybe String`s
getfields :: Data object => object -> [Maybe String]
getfields = gmapQ toString

-- Generic function to convert one field.
toString :: (Data a) => a -> Maybe String
toString = mkQ    Nothing        -- make a query with default value Nothing
                  id             -- handle:          id     :: Maybe String -> Maybe String
           `extQ` Just           -- extend to:       Just   :: String -> Maybe String

-- Get field name/value pairs from any `Data` object.
getpairs :: Data object => object -> [(String, Maybe String)]
getpairs = zip <$> getnames <*> getfields

getKey :: Data record => String -> record -> Maybe String
getKey v = lookup (Just v) . map swap . getpairs

main :: IO ()
main = do
  print $ getKey "value2" $
    HaskellRecord {
      _key1 = Just "value1",
      _key2 = Just "value2",
      _key3 = Just "value3"
      }
2 голосов
/ 05 мая 2020

Конечно, от руки можно написать. Это просто, хотя и немного утомительно и подвержено ошибкам:

getKey (HaskellRecord (Just x) _ _) y | x == y = Just "_key1"
getKey (HaskellRecord _ (Just x) _) y | x == y = Just "_key2"
getKey (HaskellRecord _ _ (Just x)) y | x == y = Just "_key3"
getKey _ _ = Nothing

Похоже, что это возможно с шаблоном Haskell или универсальными шаблонами, но нет какого-то заранее созданного способа сделать именно это, поэтому вы ' мне пришлось бы написать это самостоятельно, и это было бы (очевидно) немного сложнее. Преимущество состоит в том, что вам нужно будет создать его только один раз и не беспокоиться об изменении его при изменении типа данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...