тип тетрис - застрявший смешивающий монади c и чистый код - PullRequest
0 голосов
/ 19 января 2020

Я пытаюсь реализовать фрагменты учебника по вулкану в haskell.

Сейчас я застрял, пытаясь перевести этот код из c:

for (const char* layerName : validationLayers) {
    bool layerFound = false;

    for (const auto& layerProperties : availableLayers) {
        if (strcmp(layerName, layerProperties.layerName) == 0) {
            layerFound = true;
            break;
        }
    }

    if (!layerFound) {
        return false;
    }
}

return true; 

Пока я дошел до этой точки:

-- This has type (Int -> Text) -> Bool
let partOne = all (`elem` requiredValidationLayers) . flip map [0 .. realCount-1]
-- This has type Int -> IO Text
let partTwo i = do
        let layerProperty = advancePtr layerProperties i
        myField_ <- readStringField @"layerName" layerProperty
        pure $ toS myField_ :: IO Text

Я чувствую, что у меня есть все кусочки здесь, но также и то, что я могу идти в совершенно неправильном направлении.

Как я собрал все это вместе?

спасибо

PS: хорошо, я только что заметил, что проверка включения набора, вероятно, обратная - не имеет значения, давайте ради вопроса сделаем вид, что это на самом деле хорошо

Ответы [ 2 ]

0 голосов
/ 19 января 2020

Я собираюсь начать с некоторых предположений:

  1. У вас есть тип data Layer = ...
  2. У вас есть псевдоним типа type Name = String
  3. Вы есть некоторая функция layerName :: Layer -> Name

Я собираюсь пройтись по определению чистой функции не потому, что она ужасно важна, а потому, что она использует технику для перехода от чистого к нечистому (т.е. IO -зависимый) код.


Имеет ли данный слой заданное имя?

Во-первых, у вас есть базовая операция

strcmp(layerName, layerProperties.layerName) == 0

Это простая Haskell функция, которая принимает Name и Layer и возвращает True, если слой имеет заданное имя.

hasName :: Name -> Layer -> Bool
hasName name layer = name == layerName layer

Есть ли один из нескольких слоев, какой-либо один из нескольких имен?

Но у нас нет ни одного имени или слоя; у нас есть списки каждого. Мы можем легко справиться с этим, используя экземпляр Applicative для списков для моделирования недетерминизма.

import Control.Applicative

foo :: [Name] -> [Layer] -> [Bool]
foo = liftA2 hasName

Я не буду беспокоиться о том, чтобы дать этой функции хорошее имя, потому что оно нам не понадобится.

Теперь мы можем получить список результатов сравнения каждого имени с каждым слоем, но мы не заботимся об отдельных результатах: мы просто хотим одно значение , которое говорит, нашли ли мы совпадение или нет. Для этого мы будем использовать or :: [Bool] -> Bool.

findMatch :: [Name] -> [Layer] -> Bool
findMatch names layers = or (foo names layers)

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

findMatch :: [Name] -> [Layer] -> Bool
findMatch names layers = or (liftA2 hasName names layers)

Обработка ввода-вывода

Теперь мы предполагаем, что у нас уже есть наши списки имен и слоев в качестве чистых значений. Но что если мы этого не сделаем? Что если у нас есть только списки, считанные из файла или из другого места, так что у нас есть IO [Name] и / или IO [Layer]? Решение состоит в том, чтобы снова использовать liftA2, но вместо использования экземпляра списка Applicative мы будем использовать экземпляр IO.

ionames :: IO [Name]
ionames = ...

iolayers :: IO [Layer]
iolayers = ...

result :: IO Bool
result = liftA2 findMatch ionames iolayers
0 голосов
/ 19 января 2020

спасибо всем комментаторам, я думаю, что я получил это сейчас:)

вот так это выглядит (внутри do блока IO ()):

supportedLayers <- for [0 .. realCount-1] $ \i -> do
    let layerProperty = advancePtr layerProperties i
    myField_ <- readStringField @"layerName" layerProperty
    pure $ toS myField_ :: IO Text
return $ requiredValidationLayers `includes` supportedLayers

где

includes :: Eq a => [a] -> [a] -> Bool
includes a b = all (`elem` b) a
...