Если вы видите что-то необычное, просто сообщите мне. Skip to main content

Часть 4: Обучение управляемое компиляцией

InВ partчасти 2 and partи 3, weмы discussedобсудили someнекоторые generalобщие purposeцели ideasидей ofобучения, learning,и andувидели sawпару aих coupleприменений applications toк Haskell. ButНо atв aкакой-то certainмомент pointэтим theнеобходимо rubberвоспользоваться. meetsКак theмы road.на Howсамом doделе weизучаем reallyчто-то learnс something from scratch, like a new library?нуля?

InВ thisэтой lastчасти part,я I'llподелюсь shareсвоим myподходом approachдля toрешения solvingпроблем thisобучения. learningЯ problem.буду Iназывать refer to it asэто "CompileОбучение Drivenуправляемое Learning"компиляцией". If you want to try the idea out, you should download our Recursion Workbook! It contains 10 practice problems that are ideal for using this approach! If you haven't written any code yet, grab our Beginner's Checklist to get started with Haskell!

THE DILEMMAДилемма

ImagineПредставим. this.Вы You'veсделали madeотличный awesomeпрогресс progressв onличном yourпроекте. petВам project.нужн Youдобавить needеще toодин addкомпонент oneчтобы moreвсё componentсобрать toво pullедино. everythingВы together.используете Youвнешнюю bringбиблиотеку inв anкачестве outsideпомошника libraryи toвы helpзастряли. withВЫ thisгадаете componentс andчего you.начать. Вы подглядываете в документацию библиотеки.get stuck.Но Youэто wonderне how you're supposed to get started. You take a gander at the documentation for the library. It's not particularly helpful.помогает.

WhileТак documentationкак forдокументация блиблиотек Haskell librariesне isn'tвсегда alwaysхороша, great,но thereесть isспасительная a saving grace.благодать. Haskell isжестко strictlyтипизирован. typed.В Generally,общем, whenкогда itон compiles,собирается, itто worksэто theработает wayкак weмы expectожидаем. itНа to.крайняк Atэто leastболее thisраспространнено isв moreHaskell, commonчем inв Haskellдругих thanязыках. otherЭто languages.может Thisбыть canобоюдоострым beмечом, aпри double-edgedжелании swordизучить whenновую it comes to learning new libraries.библиотеку.

OnС theдругой oneстороны, hand,если ifвы youможете canсколотить cobbleправильные togetherтипы theдля correctфункций, typesвы forна functions,правильном you'reпути. wellОднако, onесли yourвы wayне toзнаете success.достаточно However,о ifтипах youв don'tбиблиотеке, knowто muchтрудно aboutпонять theоткуда typesначинать. inЧто theделать, library,если it'sвы hardне toзнаете knowкак whereсобирать toчто-то start.правильно? WhatВы doможете youнаписать doмного ifкода youс don'tдогадками, knowно howв toрезультате constructвы anythingполучите ofмножество theсообщений rightс type?ошибками. YouТак canкак tryвы toне writeзнакомы aс lotтипами, ofэто codeбудет andтрудно guess, but then you’ll get a mountain of error messages. Since you aren’t familiar with the types, they’ll be difficult to decipher.дешифровать.

ToЧтобы learnизучить aновую newбиблиотеку libraryили orсистему, system,вам youнужно shouldначать startс outнаписания byмаленького writingкода, asтакого, littleчтобы codeон asмог youскомпилироваться. canИдея toзваимодейстовать makeс theобечением codeуправляемое continueкомпиляцией toочень compile.просто Theдля ideasTDD. involvedДля withначала compileдавайте drivenвзглянем learningв areобщем veryна similar to test driven development. So first, let's take a quick overview of this more well-known idea.неё.

TEST DRIVEN DEVELOPMENT

Test driven development is a paradigm of software development where you write your tests before writing your source code. You consider the effects you want the code to have, and what the exposed functions should be. Then you write tests establishing expectations about the exposed functions. You only write the source code for a feature once you’re satisfied with the scope of your tests.

Once you’ve done this, the test results drive the development. You don’t have to spend too much time figuring out what piece of code you should implement. You find the first failing test case, make it pass, rinse and repeat. You want to write as little code as you can to make the test pass. Obviously, you shouldn't just be hard-coding function definitions to fit the tests. Your test cases should be robust enough that this is impossible.

Now, if you’re trying to write as little code as possible to make the tests pass, you might end up with disorganized code. This would not be good. The main idea in TDD to combat this is the Red-Green-Refactor cycle. First you write tests, which fail (red). Then you make the tests pass (green). Then you refactor your code to make it live up to whatever style standards you are using (refactor). When you've finished this, you move on to the next piece of functionality.

COMPILE DRIVEN LEARNING

TDD isэто great,пример butразработки weПО can’tгде necessarilyвы applyпишете itсначала toтесты learningпотом aсам newкод. library.Вы Ifпредполагаете youэффект, don’tкоторый knowдолжен theделать types,код, youи can’tкакой writeрезультат goodфункции tests.должен Soбыть. youЗатем canвы useпишете thisтесты processуказывая instead.ожидания Inот aфункции. way,Вы we’reпишете usingтолько theисходный typeкод systemдля andсвойства theкак compilerтолько asвы aудовлетворенны testнабором ofваших ourтестов.

understanding

Как ofтолько theвы code.это Weвыполните, canрезультаты useтеста thisведут knowledgeразработку. toВам tryне toнужно keepпроводить ourмного codeвремени compilingвыясняя, asкакой muchкусок asкода possibleвы toдолжны accomplishреализовать. twoВы goals:находите первый падающий тест, исправляете его и повторяете. Задача написать как можно меньше кода для прохождения теста. Очевидно, вы не должны просто хардкодить функцию, для прохождения тестов. Ваш тест должен быть достаточно крепким, насколько это возмоно.

Если вы пытаетесь написать код по возможности проходящий тестирование, вы можете закончить с неорганизованным кодом. Это не есть хорошо. Главная цель в TDD сражаться с циклом Red-Green-Refactor. Сначала вы пишете тесты, которые падают(Red). Делаете так, чтобы все тесты стали(green). Затем реорганизовать ваш код таким образом, чтобы он подчинялся общему стилю который вы используете. После того как это завершено, вы двигаетесь к следующему функциональному коду.

Обучение управляемое компиляцией

TDD это великолепно, но мы не можем его применять к изучению новой библиотеки. Если вы не знаете типы, вы не можете написать хорошие тесты. Поэтому нужно использовать другой процесс. В некотором роде, мы используем систему типов и компилятор в качестве теста понимаем ли мы наш код. Мы можем использовать знания чтобы сделать код, который удовлетворяет двум параметрам:

  1. Провести

    Driveнашу ourразработку developmentи andзнать, knowчто exactlyименно whatмы we’reхотим intending to implement next.

    реализовать.
  2. Избежать

    Avoidмалодушия theпри discouragingвиде “mountain"горы of errors” effect. The approach looks like this:

  3. Define the function you’re implementing, and then stub it out as undefined. (Your code should still compile.)

  4. Make the smallest progress you can in defining the function so the code still compiles.

  5. Determine the next piece of code to write, whether it is an undefined value you need to fill in, or a stubbed portion of a constructor for an object.

  6. Repeat 2-3. Notice at the end of every step of this process, we should still have compiling code. The undefined value is a wonderful tool here. It is a value in Haskell which can take on any type, so you can stub out any function or value with it. The key is to be able to see the next layer of implementation.

    ошибок".

Подход выглядит сделующим образом:

  1. Определим функцию которую реализуем, и затем сделаем заглушку как undefined. (Код должен компилироваться)
  2. Сделаем небольшие изменения определении функции, так что бы проходила компиляция.
  3. Определим следующий кусок кода, для написания, будь-то это undefined значение, которое нужно заполнить, или заглушка для конструктора объектов.
  4. Повторяем шаги 2-3.

Отметим, что в конце каждого шага этого процесса, вы должны иметь компилируемый код. Здсь значение undefined это отличный инструмент. Это значение в Haskell которое может принимать любое значение, таким образом, что вы можете сделать заглушку для любой функции или значения в ней. Ключ в том, чтобы видеть следующий уровень реализации.

CDL INна PRACTICEпрактике

Here’sВот anпример, exampleзапуска ofкода runningчерез throughэтот thisпроцесс process fromот "One Week Apps",. oneСначала ofя myопределяю sideфункцию projects.которую Firstя Iхочу defined an function I wanted to write:написать.

swiftFileFromView :: OWAAppInfo -> OWAView -> SwiftFile
swiftFileFromView = undefined

ThisЭта functionфункция saysговорит, weчто wantмы toхотим beиметь ableвозможность toпринимать take an “App Info”Info objectобъект about ourнашего Swift application,приложения, asтак wellже asкак aи View object,объект, andи generate aсоздать Swift fileфайл forдля theвида. view.Теперь Nowмы weдолжны haveопределить toследующий determineшаг. theМы nextхотим, step.чтобы Weнаш wantкод ourсобирался codeвсё toвремя compileпока whileмы stillрешаем makingпроблему. progressТип towardSwiftFile solvingэто theобертка problem.вокрук Theсписка SwiftFileтипа typeFileSection. isПоэтому aмы wrapperможем aroundсделать a list of FileSection items. So we are able to do this:так:

swiftFileFromView :: OWAAppInfo -> OWAView -> SwiftFile
swiftFileFromView _ _ = SwiftFile []

ThisИ stillон compiles!всё Admittedly,еще itкомпилируется! isПредположительно, quiteон incomplete!совершенно Butне we’veзавершен! madeНо aмы tinyсделали stepмаленький inшак theв rightправильном direction.направлении.

ForДля theследующего nextшага, step,нам weнужно haveопределить toкакие determineFileSection whatобъекты FileSectionпомещаются objectsв goсписок. intoВ theэтом list.случае Inмы thisхотим caseтри weразличных wantразделов. threeПервая different- sections.у Firstнас weесть haveраздел theкомментариев commentsв sectionверху. atВторое the- top.есть Second,раздел we"важное". haveИ anесть “imports”главный section.раздел Thenреализации. weМы haveможем theпоместить mainвыражение implementationв section.эти Soтри weсписка, canи putзатем expressionsиспользовать forзаглушку these in the list, and then stub them out below:ниже:

swiftFileFromView :: OWAAppInfo -> OWAView -> SwiftFile
swiftFileFromView _ _ = SwiftFile [commentSection, importsSection, classSection]
  where
    commentSection = undefined
    importsSection = undefined
    classSection = undefined

ThisЭтот codeкод stillвсё compiles.еще Nowкомпилируется. weТеперь canмы fillможем inзаполнить theраздел sectionsпо one-by-oneочереди, insteadвместо ofтого, burdeningчтобы ourselvesнапрягаться withнаписанием writingкода allцеликом. theКаждый codeиз atразделов once.имеет Eachсвою willкомпонентную haveчасть, itsкоторую ownмы componentразобъем parts, which we’ll break down further.дальше.

UsingИспользуя ourнаши knowledgeзнания ofо theтипе FileSection, type,мы weможем canиспользовать useконструктор theBlockCommenSection. BlockCommentSectionОн constructor.просто Thisпринимает justсписок takesстрок. aТак listже, ofмы strings.воспользуемся Likewise,конструктором we’llImportSection useдля theимпорта ImportsSectionраздела. constructorОн forтак theже importsпринимает section.список. ItПродолжим alsoследующим takes a list. So we can make progress like so:образом:

swiftFileFromView :: OWAAppInfo -> OWAView -> SwiftFile
swiftFileFromView _ _ = SwiftFile [commentSection, importsSection, classSection]
  where
    commentSection = BlockCommentSection []
    importsSection = ImportsSection []
    classSection = undefined

SoИ onceснова again,наш ourкод codeкомпилируется, stillа compiles,мы andв we’veсвою madeочередь smallсделали progress.небольшой Nowпрогресс. we’llТеперь determineопределим whatкакая stringsстрока weнам needнужна forдля theраздела commentsкомментарив, section,и andдобавим addеё. those.Теперь Thenможно weдобавить canImport addобъектов theдля Importраздела objectsimports. forЕсли theчто-то importsпойдет section.не Ifпо weплану screwмы up,увидим we’llтолько seeодну aошибку singleи errorмы messageбудем andзнать we’llгде knowона exactlyпроисходит. whereЭто theделает issueпроцесс is.разработки Thisгораздо makes for a much faster development process.быстрее.

SUMMARYЗаключение

WeМы talkedпоговорили aboutо thisподходе approachизучения forновых learningбиблиотек, newно libraries,это butподходит it’sи greatк forобычной normalразработке. developmentИзбегайте asжелания well!уходить Avoidс theголовой temptationи toписать diveсразу inсотни andстрок writeкода! severalВы hundredпожалеете linesоб ofэтом code!когда You’llувидите regretкучу itсообщений whenоб dealingошибке! withНеспешность dozensи ofтвердость errorпозволит messages!выйграть Slowгонку. andВы steadyвыполните trulyгораздо doesбольше winесли theразобъете raceна here.маленькие You’llдетали, getи yourвоспользуетесь workкомпилятором doneв muchкачестве fasterпроверки ifвашего you break it down piece by piece, and use the compiler to sanity check your work.кода.

If you want to take a stab at implementing Compile Driven Learning, you should check out our free Recusion Workbook! It has 10 practice problems that start out as undefined. You can try implement them yourself step-by-step and see if you can get the tests to pass!

If you’ve never written any Haskell before, download our Beginner's Checklist. It’ll tell you everything you need to know about writing your first lines of Haskell!