Очень сложно добавить тип к шагу. Если мне это удастся, я дам вам знать ...

онкретная проблема на самом деле не в общем переводе OO-интерфейса на Haskell. Это просто лучшее название, которое я мог придумать. Тем не менее, я уверен, что моя проблема возникла из-за все еще плохого понимания кода моделирования с помощью Haskell и мышления, все еще находящегося в стране парадигм OO (вы все еще новичок в Haskell, вы видите).

Я пишу симулятор Mastermind (вариация), чтобы проверить работоспособность нескольких стратегий Mastermind. На самом деле, я уже сделал это вЯва а такжеLua и таким образом этоВерсия на Haskell для меня это всего лишь упражнение, чтобы научиться программировать на Хаскеле. Вы можете проверить readme версии Lua / Java, если вам интересно, чего я пытаюсь достичь в конце.

Но теперь для моей конкретной проблемы (вкратце и в терминах ОО): я хочу предоставить интерфейс для стратегий, чтобы я мог взаимозаменяемо поместить стратегию, которая придерживается этого интерфейса, в рекурсию моделирования (цикл) и после того, как это сделано, получить некоторые данные о производительности стратегии. Кроме того, я хочу, чтобы стратегия поддерживала произвольное состояние, и я не хочу заботиться о том, какое состояние поддерживает каждая стратегия. Но именно это решение, которое на самом деле необходимо, все усложнило. Другое требование, которое конкретно привело к описанной ниже проблеме, заключается в том, что имя стратегии может быть предоставлено в качестве аргумента командной строки, а затем выполняется симуляция с этой конкретной стратегией.

Сначала я счел класс типов подходящим для этих требований, но, не придумав реальной идеи, как моделировать код таким образом, я отказался от этой идеи. Затем я выбрал ADT, использовал его до сих пор и дошел до кода.

Итак, поверхностный вопрос - как решить проблему, которую я предлагаю ниже. Более глубокий вопрос заключается в том, как лучше моделировать мои требования к интерфейсу с произвольным состоянием в Haskell.

Вот сокращенная и адаптированная выдержка из моего кода:

-- reduced & simplified example
import Control.Monad.State

type Code = [Int]

data Answer = Answer { 
    blacks :: Int, 
    whites :: Int 
    } deriving (Eq, Show)

-- As you can see I decided for a type variable a that
-- represents the arbitrary state a strategy might carry
-- around. I wonder if this is the right way to do it.
-- | This is the interface for a strategy. A strategy must provide a method 
-- that, given a mastermind answer, returns the next guess, an initial state 
-- and the very first guess.
data Strategy a = Strategy {
    firstGuess :: Int -> Code,
    initialize :: Int -> a, -- a "constructor" in disguise
    guess      :: Answer -> State a Code
    }

dummy = Strategy {
    firstGuess   = firstGuess',
    initialize   = initialize', 
    guess        = guess'
    }

-- | The very first guess is always T0,T1,...,Tx, where x is the code length.
firstGuess' :: Int -> Code
firstGuess' length = [0..length-1]

-- | Memorize the code length
initialize' :: Int -> Int
initialize' = id

-- | Always return the same guess
guess' :: Answer -> State Int Code
guess' = const $ liftM firstGuess' get

-- HERE IS THE PROBLEM
-- I need this function since I'll get the name of a strategy
-- as a string from the command line and need to dispatch the
-- correct strategy to the simulation. Note, that there would
-- be of course more pattern matches for different strategies
-- with different accompanying states a.
nameToStrategy :: String -> Strategy a
nameToStrategy "dummy" = dummy

Выполнение файла выдает следующее сообщение об ошибке:

Prelude> :l Problem.hs
[1 of 1] Compiling Main             ( Problem.hs, interpreted )

Problem.hs:37:25:
    Couldn't match expected type `a' against inferred type `Int'
      `a' is a rigid type variable bound by
          the type signature for `nameToStrategy' at Problem.hs:36:37
      Expected type: Strategy a
      Inferred type: Strategy Int
    In the expression: dummy
    In the definition of `nameToStrategy':
        nameToStrategy "dummy" = dummy
Failed, modules loaded: none.

Я вроде могу понять проблему интуитивно. Кажется, проблема в том, чтоnameToStrategy не может просто вернуть стратегию с каким-то государствомa, Переменная типа должна быть конкретной, так как если я изменю типnameToStrategy вString -> Strategy Int все в порядке. Но это не решение моей проблемы.

Я решил, что мне нужно расслабиться типа. Тем не менее, я не знаю, как это сделать. Я слышал оData.Dynamic и экзистенциальные типы, и они могут помочь мне. Тем не менее, я чувствую, что при лучшем моделировании моего кода они мне не понадобятся.

редактироватьВ конце концов мне удалось включить в код предложения sclv, и теперь это стало намного лучше. Код для стратегий более понятен, так как мне больше не нужен особый случай для первого предположения, и я могу использовать охрану, чтобы лучше различать случай правильного и неправильного предположения. Основная обработка симуляции не так элегантна, как версия sclv, так как я поставилstepState (и функции, использующиеstepState) в монаду ввода-вывода для измерения времени вычислений и, следовательно, наличия некоторого «монадического синтаксического шума». Возможность легко смоделировать пару шагов моделирования (что раньше было невозможно) помогла мне найти взаимный рекурсивный бесконечный цикл (эту ошибку было странно понимать). В целом, код теперь кажется более дискретным. Излишне говорить, что мне не нужноunsafeCoerce взломать больше, чтобы отправить имена в стратегии (или лучше "упакованные стратегии"). Я надеюсь, что когда-нибудь функциональный образ мышления придет и ко мне.

Ответы на вопрос(2)

Ваш ответ на вопрос