Search
Write a publication
Pull to refresh
3
0

Пользователь

Send message
Literate .vimrc
Сначала, у меня был один .vimrc to rule them all, но когда плагинов становится больше дюжины, становится сложно отслеживать настройки. Некоторые опции (deoplete) надо было настраивать до загрузки плагинов, некоторые — после. Что-то настраивалось через `let g:...`, что-то через `call plugin#function(...`. Потом я разбил на много файликов, отдельно для каждого плагина и source'ил их из .vimrc — стало легче читать, но сложнее редактировать. С Literate .vimrc стало возможным накидать markdown-якорей по конфигу, заголовков, нумерованных списков, e.t.c., а потом просматривать всё в браузере через markdow-preview.
Вообще, это не круто выкладывать решения с открытой площадки. Не знаю, на ком они зарабатывают, но проходить таски по гайду — фу.
Если совсем не идёт, можно попросить совета на форуме/ирке/написать в личку.
Ещё вариант — активная подмена сертификата и безрассудный пользователь.
Я слышал, что в одной вселенной люди умеют решать задачи на логику, не притягивая свои иррациональные идеологические суждения. В этой вселенной люди читают этот абзац так:
«Докажем, что существует недвудольный граф, в котором нельзя выбрать стабильную комбинацию пар. Пусть его матрица весов выглядит так:
* 3 2 1
2 * 3 1
3 2 * 1
1 1 1 *
Тогда мы не можем выбрать пары, стабильные согласно определению выше.»
но по этой же логике вам надо писать тесты на фреймворк тестов.

Вообще-то надо. Тесты — код первого порядка и должен подчиняться всем требованиям релизного кода: стили, ревью, ограничения на выбор зависимостей (и расширений в случае Хаскеля) и наличие тестов.
Другое дело, что писать тесты на внешний код — ну такое.
А можно ссылку на .vimrc?

А вообще зачётно, в духе постов Aphyr.
Так это ж и получается Dyn ctx, который получился в самом конце, разве нет?
Разве что, можно явно Constraint kind не указывать, тайпчекер выведет.

Точно, просмотрел.
Хм, что-то у меня это не очень сработало, тайпчекер всё равно не видит a1, a2 в rhs, даже несмотря на ScopedTypeVariables. Там нет ли того, что можно упоминать только те переменные, которые были объявлены с forall в сигнатуре функции?

Неаккуратно, написал, сорян:
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}

import Prelude
import Data.Typeable

data Wrapper cxt where
  Wrap :: (Typeable a, cxt a) => a -> Wrapper cxt

withDyns :: (forall a. Ord a => a -> a -> b)
         -> (TypeRep -> TypeRep -> b)
         -> Wrapper Ord -> Wrapper Ord -> b
withDyns f def (Wrap (v1 :: a1)) (Wrap (v2 :: a2)) = case eqT :: Maybe (a1 :~: a2) of
  Nothing -> def (typeOf v1) (typeOf v2)
  Just Refl -> f v1 v2

main :: IO ()
main = do
  let a = Wrap True
  let b = Wrap False
  let c = Wrap 'a'
  print $ withDyns (\x y -> show $ x `compare` y) (\x y -> show x ++ show y) a b
  print $ withDyns (\x y -> show $ x `compare` y) (\x y -> show x ++ show y) a c

$ runghc main.hs
"GT"
"BoolChar"

Добавил TypeOperators, Typeable constraint в Wrapper и поправил тип функции def.

Вообще, я лично не очень люблю интроспекцию типов, свой код в итоге перегрузил кучей вызовов cast, а потом долго ловил ошибку, вызванную cast'ом функции. Сейчас склоняюсь к мысли, что если пространство возможных типов не слишком велико, то проще работать с большим алгебраическим типом, a la, «data ArrayItem = C Char | I Int | B Bool...». Для работы с динамическими типами нужно крайне тщательно спланировать дизайн, потому что расширять его потом может быть очень мучительно, и нужно очень хорошо понимать, что сделать принципиально возможно, а что нет.
Можно немного проще, не добавляя TypeRep в обёртку, при помощи ScopedTypeVariables (не увидел объявления SomeTypeRep, кстати):
data SomeOrd where
  MkSomeOrd :: Ord a => a -> SomeOrd

withDyns :: (forall a. Ord a => a -> a -> b) ->
            (SomeTypeRep -> SomeTypeRep -> b) ->
            SomeOrd -> SomeOrd -> b
withDyns f def (SomeOrd (v1 :: a1)) (SomeOrd (v2 :: a2)) = case eqT :: Maybe (a1 :~: a2) of
  Nothing -> def (typeOf v1) (typeOf v2)
  Just Refl -> f v1 v2

Ещё советую посмотреть статью про гетерогенные списки на wiki.haskell, там интересная техника с обёртыванием constraint'а, чтобы не заводить отдельный тип Some для каждого ограничения
data Wrapper (con :: Constraint) where
    Wrap :: (con a) => a -> Wrapper con
withDyns :: (forall a. Ord a => a -> a -> b) ->
            (Wrapper Ord -> Wrapper Ord -> b) ->
            SomeOrd -> SomeOrd -> b
withDyns f def (Wrap (v1 :: a1)) (Wrap (v2 :: a2)) = case eqT :: Maybe (a1 :~: a2) of
  Nothing -> def (typeOf v1) (typeOf v2)
  Just Refl -> f v1 v2
Мне кажется, тут смешали две группы людей: будущие профессионалы-программисты и условные «домохозяйки», которые хотят себе себе сайт-визитку или малинку с чайником связать. Вот для первых тезис «пытаться изучать тестирование плюс что-либо ещё — ошибка» я бы развил в «следовательно они должны начинать с тестирования». Тестирование очень сильно способствует элегантности кода, а правило «тесты — код первого порядка и подчиняются тем же требованиям, что и основной код» способствует элегантности тестов.
Есть ещё известный эффект, что мотивация сильно падает, если долгое время не видно результата — если стек глубокий, а до hello world'а ещё пилить и пилить. Так вот, если параллельно делать тесты, то эффект сильно слабеет, во всяком случае для меня лично.
Спасибо.
У вас псевдокод во фреймах с подсветкой Питона, после первой кавычки всё зеленеет. Попробуйте поиграть с подсветкой, а лучше поменяйте на честный Питон, все всё поймут :)
Это то что я назвал вторым способом. При этом мы теряем информацию, в каком конкретно вызове произошло исключение и должны поддерживать несколько обработчиков, что затрудняет чтение и добавляет сложности.
Что насчёт такого?
def foo(a, b, dictionary, host):
    j = divide(a / b)
    l = http.get(host).parseSmth()
    k = dictionary[l]
    m = divide(k / j)
    ...

Здесь у нас могут быть разные исключения — арифметика, сеть, отсутствует ключ. Либо мы вешаем на весь блок «except Exception», либо делаем серию except'ов на каждый тип исключения, либо вешаем персональный try на каждый опасный вызов. Второй и третий способ раздувает код и делает его нелинейным, первый я не приемлю, потому что слишком общий. Монада Result добавляет один декоратор за пределами тела функции и по .unwrap() в конец каждой строки.
Вторая проблема — два опасных divide в одной функции. Допустим мы поставили общий «except ZeroDivisionError», но теперь мы не знаем, какой из них бросил исключение. Монада Result позволяет перед вызовом unwrap() сделать rescue() и добавить подробное описание.
Если я правильно понял — завернуть всё в pipeline и везде где мы получили эти переменные j, l, n, k, z получать их unwrap'ом. Тогда в самих переменных будут честные int'ы, но если хотя бы одна завалится, то pipeline тормознёт вычисления.
В Haskell есть какая-то похожая штука для неявной работы с контейнерами? inline-разворачивания, так сказать?

Конечно — трансформеры монад. Делаем всякие разные грязные действия, возвращающие Either (Result в статье), если хотя бы одно вернёт Left (Failure), игнорируем все последующие и возращаем этот Left. При этом локальные присваивания игнорируют Either (Result) и всегда думают, что им вернули Right (Success). Если не вернули — смотри выше.
Вроде бы и да, но в этой статье я вижу другой смысл. В RP коллбеки очень тонкие и не порождают новые коллбеки (они могут, но это не идиоматично). Их задача — получить сообщение и послать его в граф. В статье описывается функция которая занимается вычислениями, выводом и вводом, хотя в RP все три части разделены.
Как такой же фрагмент кода будет работать в реактивном стиле? Нить исполняет вычисления, посылает HTTP-запрос и вместо того чтобы заблокироваться и при получении результата синхронно обработать его, описывает код (оставляет callback) который должен быть исполнен в качестве реакции (отсюда слово реактивный) на результат. После этого нить продолжает работу, делая какие-то другие вычисления (может быть, как раз обрабатывая результаты других HTTP-запросов) без переключения контекста.

Это не реактивное, а асинхронное программирование на коллбеках. Хотя реактивные фреймворки зачастую имеют асинхронный реактор, но одной из фич реактивного программирования является как раз избегание коллбэков. Реактивное программирование — это прежде всего про потоки данных внутри системы и распространение изменений. Никаких отложенных вызовов не предполагается. За манифест спасибо, посмотрю.
Пришла мысль, что геймдев идёт по стопам музыкальной и писательской индустрии. Огромное количество авторов, магазины/лейблы/издательства. Всё те же плюсы и минусы: и снижение порога входа и жадные издатели и огромное количество мусора. Жаловаться бессмысленно, зато можно предугадать некоторые будущие шаги.
Спасибо, Хаскелю, на мой взгляд, очень не хватает материала про то как из <непонятная хрень из алгебры> сделать что-то полезное.
Увы, но из новых аминокислот получить улучшенные органы человека не более просто, чем из новых инструкций процессора получить искусственный интеллект. Слишком много запутанных логических уровней в промежутке.
Все задачки на Forensics во многих ctf?

Information

Rating
3,736-th
Location
Россия
Registered
Activity