1. Haskell / Говнокод #11976

    −82

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    69. 69
    70. 70
    71. 71
    72. 72
    73. 73
    74. 74
    75. 75
    {-# LANGUAGE ExistentialQuantification,
                 DeriveDataTypeable,
                 PatternSignatures #-}
    
    import Data.Typeable
    import Control.Concurrent
    import Control.Concurrent.MVar
    import Control.Concurrent.Chan
    
    -- Core data types
    
    data Message = forall t . Typeable t => Message t | StopMessage
        deriving Typeable
    
    data Handler = forall t . Typeable t => Handler (t -> IO ())
    
    
    -- Worker thread
    
    data Worker = Worker (Chan Message) (MVar ())
    
    workerThread :: [Handler] -> Chan Message -> MVar () -> IO ()
    workerThread handlers chan finish = loop where
        loop = do
            message <- readChan chan
            case message of
                StopMessage -> putMVar finish ()
                Message val -> do
                    foldr (tryHandler val) (putStrLn "Unhandled message") handlers
                    loop
        tryHandler val (Handler h) rest = maybe rest h (cast val)
    
    startWorker :: [Handler] -> IO Worker
    startWorker handlers = do
        chan <- newChan
        finish <- newEmptyMVar
        forkIO (workerThread handlers chan finish)
        return $ Worker chan finish
    
    send :: Typeable m => Worker -> m -> IO ()
    send (Worker chan _) message = do
        writeChan chan $ Message message
    
    stopWorker :: Worker -> IO ()
    stopWorker (Worker chan finish) = do
        writeChan chan $ StopMessage
        takeMVar finish
    
    
    -- Some tests
    
    data Test = Test Bool String deriving Typeable
    
    intHandler :: Int -> IO ()
    intHandler val = putStrLn $ "Int: " ++ show (val * 2)
    
    strHandler :: String -> IO ()
    strHandler val = putStrLn $ "String: " ++ reverse val
    
    testHandler :: Test -> IO ()
    testHandler (Test b s) = putStrLn $ "Test: " ++ show b ++ " " ++ show s
    
    main = do
        w <- startWorker [
            Handler intHandler,
            Handler (\(val::Char) -> putStrLn $ "Char: " ++ show val),
            Handler strHandler,
            Handler testHandler]
        send w (5::Int)
        send w False
        send w 'a'
        send w "foo"
        send w (Test True "bar")
        stopWorker w
        putStrLn "Finished!"

    Вот такая вот портянка была написана под влиянием дискуссии с HaskellGovno http://govnokod.ru/11968, и недавней его просьбой рассказать об общении потоков в хаскеле.

    Код запускает тред, в который можно передавать различные сообщения (ограничение только одно - тип сообщения должен быть инстансом тайпкласса Typeable). В треде исполняются указанные хендлеры, каждый из которых ловит свой тип сообщений.

    P.S. Для неимеющих хаскеля, но желающих посмотреть на работу кода: http://ideone.com/OMVamc.

    Запостил: bormand, 22 Октября 2012

    Комментарии (43) RSS

    • >портянка была написана под влиянием HaskellGovno
      Очень красиво. :) Декларативно. А как этот паттерн называется? Планировщик на основе статически типизированных сообщений?
      Ответить
    • Я у них поел, но выглядит просто круто.
      Ответить
    • Получается Int и прочие стандартные типы по дефолту отнаследованны от интерфейса (Typeable a => a)... Интересно, оно переголову даёт какую-нибудь?
      Ответить
      • Не отнаследованы от Typeable, а принадлежат Typeable. Если мне не изменяет память, оверхед там только при использовании типов данных с forall, в остальных случаях компилятору и так все ясно.
        Ответить
    • Это ты написал какой-то конвейер с нелимитированной очередью-списком, которая типа канал.
      А хендлеры - это вновь изобретенный полиморфизм или же мультиметоды, если хотите.
      >StopMessage -> putMVar finish ()
      А без отравленных сообщений никак нельзя? Недолюбливаю я эти пилюли.

      Ничего сверхгодного в вашем бомжественном хацкиле нету, я такую же хреновину в императивном стиле напишу за 15 минут с библиотечной очередью.
      Ответить
      • > А хендлеры - это вновь изобретенный полиморфизм или же мультиметоды, если хотите.
        Они самые. В том же лиспе это нативно работает.

        > А без отравленных сообщений никак нельзя? Недолюбливаю я эти пилюли.
        Поясните плиз, что не так со StopMessage?

        > я такую же хреновину в императивном стиле напишу за 15 минут с библиотечной очередью
        Да я не спорю ;). На питоне всяко и за 5 можно управиться.
        Ответить
        • >Поясните плиз, что не так со StopMessage?
          Да как-то некошерно в очереди с данными передавать управляющие конвеером сообщения.
          volatile boolean terminated=false;
          Ответить
          • > Да как-то некошерно в очереди с данными передавать управляющие конвеером сообщения.
            > volatile boolean terminated=false;
            Так с этим булом потом начинаются проблемы - надо одновременно опрашивать и бул и очередь. Как это сделать в хаскеле - я х.з.

            Имхо даже в императивном языке проще кинуть особое сообщение для остановки треда, чем городить этот огород с булом и его опросом.
            Ответить
            • >надо одновременно опрашивать и бул и очередь. Как это сделать в хаскеле - я х.з.
              Отож. И я об этом.

              >даже в императивном языке проще кинуть особое сообщение для остановки треда
              Если потребителей много - не проще. Или надо делать одного потребителя и тредпул, которому он раздает задания.
              Конечно если других средств коммуникации помимо канала нету - то символ "Конец передачи" неизбежен.

              PS. Херня какая-то с говнокодом. Сессии на главной нету, а на остальных есть.
              Ответить
              • > Если потребителей много - не проще.
                Как вариант - если у нас N воркеров - вбросить N сообщений об остановке. Каждый получит ровно по одному. Минус способа - надо знать сколько именно запущено воркеров.
                Ответить
                • >Как вариант - если у нас N воркеров - вбросить N сообщений об остановке.
                  Ну ведь потому и костыль. В данном случае оно вылазит наружу во всей красе.
                  Говно всплывает к верху, так сказать.
                  Переменная, а зачастую какой-то енум с состояниями INIT,RUN,STOP,FAIL гораздо лучше.
                  Ответить
                • ахаха он сказал "вбросить"
                  Ответить
                  • Напоминает Бивиса и Баттхеда
                    - Баттхед, ты слышал, он сказал: "прибор!".
                    - А еще он сказал "канал"! Гы-гы-гы.
                    Ответить
                  • есть пруфы что ты ТарасБ?
                    Ответить
                    • Как-то раз, в одном из трамваев я увидел надпись "давайте уедем жить в Питер", выцарапанную на спинке кресла каким-то отчаянным романтиком. Наверно, это был тарас Б.
                      Ответить
        • >Да я не спорю
          тогда непонятно в чем крутота хацкила, если всё то же, но только хуёвей.

          Он по идее должен сам как-то всё асспараллелить и предоставить какую-то сверхудобную реализацию producer-consumer отношений.
          Ответить
      • >Ничего сверхгодного в вашем бомжественном хацкиле нету, я такую же хреновину в императивном стиле напишу за 15 минут

        Я расскажу тебе свою историю.

        Я учился в университете, по специальности программист. Да, я не был в группе самым одарённым, но я пытался что-то делать. Нам преподавали C++. Я не очень это всё понимал. Мои одногруппники были не очень хорошими людьми, и всегда не воспринимали меня и даже ущемляли. Преподы тоже не упускали своего и говорили, что только тупые не могут понять C++. Я не был тупой, я просто не понимал зачем всё это нужно?

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

        И вдруг я услышал разговор двух молодых парней, которые выходили из новой машины, они говорили про программирование. Они были классно одеты. У них было много денег. Я хотел быть ими. Я подошёл к ним и спросил:
        — Как мне стать таким как Вы?
        Они улыбнулись и сказали: “Пошли с нами”.
        Ответить
        • Мы зашли в ближайшее кафе, один из них достал ноутбук и показал его — Haskell. Я по-настоящему понял. Я влюбился. Я был счастлив. Я пошёл домой, я прочитал haskellwiki, я накачал книг с amazon.com, я начал читать. Да я уже не ходил на занятия, мне они были уже не нужны. Потом мне позвонили из деканата и сказали, что отчислят меня, если я не сдам курсовую. Мне было наплевать, что меня отчислят, но курсовой, я его сдам, ведь у меня есть новое знание.

          Когда я пришёл на защиту, то там собралась вся группа.

          Они подходили к компьютерам и демонстрировали применение паттернов и шаблонов, наследование и прочее, всё что на самом деле не нужно.

          Потом настала моя очередь. Я встал и медленно шёл. Они смеялись и кричали — что не написал видать?

          Когда я показал этот код, который делал одной строчкой то, что у них было в 25 классах, они замолчали. Я встал и ушёл.

          Говорят препод ушёл с работы из-за того, что свихнулся, он не мог поверить что его C++ не нужен.

          Я теперь иногда телефоны своих одногруппников в газете объявлений, где они готовы работать за еду. Но мне пофиг, я знаю Haskell, что приносит мне тысячи долларов.
          Ответить
          • 1. Когда вижу портянку в полтора экрана - сразу ясно что это не твой текст.
            2. Тухло, ведь я уже читал эту пасту.
            3. Она неуместна - в примере 50 строк потрачено на конвейер.
            Ответить
            • > 1. Когда вижу портянку в полтора экрана - сразу ясно что это не твой текст.
              +

              > 2. Тухло, ведь я уже читал эту пасту.
              +

              > 3. Она неуместна - в примере 50 строк потрачено на конвейер.
              Ну не на сам конвейер, в качестве которого тут использовался готовый Chan, а на тред, который этот конвейер использует.
              Ответить
            • Я пасту не видел, она доставила, я плюсанул. Твой ответ тоже.
              Ответить
              • у меня тоже есть история:

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

                и мне это все было интересно и сложно, но я не стал золотомедалистом по одной причине: еще на первом курсе нам преподавали самые основы поцкаля. на первую же лабу я постарался:
                слабал на трубопоцкале игрулю тетрис, со всеми наворотами: и режим 320х200х256 и ассмовставки, и графоэффекты типа шторок и затемнения, и уровни, и спецфигуры - полный блекджек. и даже шлюхи были после каждого пройденного уровня. не было только музыки по причине отсутствия колонок.
                я был глуп и наивен, с широко открытыми глазами, полными желания постигать и творить круть несусветную. но жизнь меня обломала, и я стал учиться из рук вон плохо, так и не окончив: препод покивал головой, позыркал на шлюх, и сказал:

                - это все очень хорошо, но для нашей программы этого всего не нужно.
                Ответить
        • ...и привели тебя в офис Вкнотакте
          Ответить
    • Кстати, понял, почему работает фишка с типизацией хендлеров в контейнере?
      Ответить
    • Годно, годно. Выкуси, scala.actors.
      Ответить
    • У меня нубский вопрос: А вот на Хокаге.хаскел.орг или на каких-то других сайах с документацией часто упоминается какие-то правила laws. Что это означает и как это выражается в коде Хаскеля? Почему для этого отдельный термин используют laws (законы)?
      Ответить
      • Это же законы. Как комутативность и асоциативность в матане.
        Ответить
        • Они в коде стандартной библиотеки как-то выражаются? Например проверяются ассертами?
          Ответить
          • У тебя есть возможность проверить. Сорцы есть на сайте.
            Ответить
    • А что делать тем, у кого нет хачкеля и кто не желает видеть ни результата работы кода, ни хачкеля?
      Ответить
    • >недавней просьбой HaskellGovno рассказать об общении потоков в хаскеле.
      Спасибо. :)
      Ответить
    • показать все, что скрытоvanished
      Ответить
      • Любой человек в здравом уме видит неоспоримые преимущества «Haskell».
        Ответить
        • показать все, что скрытоvanished
          Ответить
          • Это означает, что людей в здравом уме не так уж и много.
            Ответить
            • Кстати, запиши, пожалуйста, функцию :: (Double, Double) -> (Double, Double) -> Double, вычисляющую расстояние между двумя точками на плоскости по классической формуле так, чтобы в записи тела функции не было скобок, кроме, возможно, скобок, превращающих инфиксные функции в префиксные.
              Ответить
              • Не знаю, как это делается на хаскеле, но знаю как на J:
                dist =: [:%:@+/[:*:-
                   NB. или
                   require 'primitives'
                   dist =: cap sqrt atop plus insert cap square minus
                   2 3 dist 4 6
                3.60555
                   2 3 dist 2 3
                0
                   2 2 dist 6 2
                4
                Ответить
                • dist =: [: %:@+/ [: *:@+.-
                     NB. или
                     dist =: cap sqrt atop plus insert cap square atop realimaginary minus
                     2j3 dist 3j2
                  1.41421
                     2j3 dist 2j3
                  0
                     2j2 dist 2j4
                  2
                  Спасибо, я кончел.
                  Ответить
              • Это на степике такая задача есть? Специальная олимпиада объявляется открытой?
                import Data.STRef
                import Control.Monad.ST
                import Data.Complex
                
                -- Level: Beginner
                -- Well, there are no parantheses...
                dist1 x y = let dx = fst x - fst y; dy = snd x - snd y in sqrt $ dx^2 + dy^2
                
                -- Level: Math geek
                -- Ha, I know complex numbers
                dist2 x y = magnitude $ uncurry (:+) x - uncurry (:+) y
                
                -- Level: Former imperative programmer
                dist3 x y = runST $ do
                  ref <- newSTRef $ (^2) $ fst x - fst y
                  modifySTRef ref $ (+) $ (^2) $ snd x - snd y
                  sqrt <$> readSTRef ref
                P.S. придумал забавный способ сконвертировать пару в список, но не придумал, как его красиво использовать на двух парах, чтобы заюзать sqrt . sum . map (^2) . zipWith (-)
                :t uncurry (:) . fmap pure
                uncurry (:) . fmap pure :: (a, a) -> [a]
                Ответить

    Добавить комментарий