LINUX.ORG.RU

Анализ пользователей Common Lisp и Racket

 , ,


11

7

Common Lisp разрабатывался и используется в предположении, что пользователь программы — программист. Поэтому из языка намеренно исключены сложные для понимания конструкции (пользователь не обязательно квалифицированный программист), поэтому в языке мощнейший отладчик, позволяющий без остановки программы переопределять функции и вообще делать что угодно. Но из-за этого документация по большей части библиотек Common Lisp существует только в виде docstring и комментариев в коде (некоторые вообще считают, что код сам себе документация). Из-за этого обработка ошибок почти всегда оставляется на отладчик (главное сделать рестарт «перезапустить с последней итерации», а там пользователь сам разберётся). Из-за этого в программе проверяется только happy path (пользователь ведь «тоже программист»).

Racket разрабатывался и используется в предположении, что пользователь программы не программист, а задача разработчика написать программу так, чтобы она корректно работала при любых входных данных (если данные некорректны, то сообщала об этом в том месте, где данные были введены). Поэтому в языке эффективная библиотека для написания тестов, система контрактов на уровне модулей, макимально широкий спектр инструментов программирования (разработчик должен быть профессионалом!). Также реализована идея инкапсуляции: считается, что пользователь модуля не должен знать особенности реализации и, более того, не может в своём коде изменить функцию чужого модуля если это явно не разрешено разработчиком того модуля. Исходный код разумеется доступен, но его не требуется смотреть, чтобы использовать модуль. Достаточно документации. Поэтому реализована мощнейшая система документировния Scribble, а при реализации макроса есть возможность обеспечить указание на ошибки в коде, предоставленном макросу пользователем, не показывая потроха макроса.

И поэтому в Racket нет CLOS (есть как минимум две реализации, но не используются) - провоцирует заплаточное программирование (monkey patching), поэтому отладчик намеренно ограничен (если ты отлаживаешь программу, значит ты не знаешь как она должна работать!), поэтому нет разработки в образе (image based) - она провоцирует разработку через отладку (а значит непонимание программы и проверку только happy path).

Таким образом, Racket и Common Lisp несмотря на внешнее сходство являются очень разными языками. И я рекомендую писать на Racket, если только конечными пользователями программы не являются исключительно программисты на Common Lisp.

Взято с http://racket-lang.blog.ru/#post214726099

Хотелось бы знать, что по этому поводу думают пользователи ЛОРа. А также, мне кажется, что для Java и C++ будет где-то такая же разница.

★★★★★
Ответ на: комментарий от monk

f «ok» = refresh >> return «ok» f _ = return Nothing

readln >>= f

не?

anonymous
()
Ответ на: комментарий от monk

ResponceOrNothing это просто Maybe (Responce)

в моём варианте data Responce a b 'a' - отвечает за успешный результат, а 'b' за тип ошибки (и вообще этот тип изоморфен Either), т.е. под разные варианты ошибок у нас подходит один тип данных.

Так же в erlang все 'ok' идеентичны насколько я понимаю, т.е. data Ok = Ok хватит или просто (), как используется в cloud haskell.

Nothing2 тоже не нужно, т.к. Nothing является общим нулём для всех Maybe (не знаю правильного термина из CT).

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

Кстати можешь посмотреть как в cloud haskell все это выглядит, а хорошие примеры ошибок в network-transport.

Про функции:

send(Socket, Packet) -> ok | {error, Reason}

send :: Socket -> Packet -> IO (Either Ok Reason)

get_closest_pid(Name) -> pid() | {error, Reason}

не уверен, что я правильно распарсил, но скорее всего:

getClosestPid :: Name -> IO (Either (IO Pid) Reason)

qnikst ★★★★★
()
Ответ на: комментарий от monk

а.. я ж вроде в том треде отвечал. Щас пробую объяснений докинуть.

Начнем с типа, у тебя это будет выполняться в контексте X, т.е. тип будет: X (Maybe String).

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

Существует 2 способа (*) поднимать операции из одной монады в другую:

1. монадические стеки, это если монада имеет специальный тип data MyMonad m a, где m это любая внутреняя монада и предоставлят операцию поднятия действий из внутренней монады в текущую

2. монада может прикидываться другой, т.е. instance MonadFoo Bar, + реализация нужных методов позволит в монаде Bar использовать действия из Foo.

На самом деле эти 2 варианта часто поставляются вместе, но нам это не важно.

Как видно из типа X не является монадическим трансформером, зато имеет инстанс MonadIO X, с методом liftIO :: IO a -> X a, т.е. позволяет поднять действие из IO в X.

Т.е. эту информацию мы получаем просто смотря на хаддоки X, или даже вызывая :info X в ghci.

так вот имея liftIO и getLine мы можем сделать liftIO getLine :: X String - половина работы сделано, чтобы «вытащить» значение из X, нужно в контексте X, сделать bind (>>=) или при do-синтаксисе (<-) дальше обычный do синтаксис (указания типов не нужны я для понимания добавил)

foo = do s <- liftIO getLine :: X String -- вытащили строковое значение  
         if s == "ok"
         then refresh >> return (Just "ok") -- можно return $ return "ok" если хочется вспомнить, что Maybe это монада
         else return Nothing -- если хочешь запутать читателя то - return $ fail "ok"

доходчиво?

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

(*) способов на самом деле чуть больше, есть какое-то древнее layers, есть extendable-effects (весьма интересная концепция), есть какой-то ад на TH (тоже может быть эффективным т.к. ты собираешь из кусков супермонаду, а не разделяешь эффекты), так же есть mtl - автоматизирующие поднятие по стеку трансформеров, а ещё это по разному можно реализовать. Но это такие тонкости, в которые можно не вдаваться почти никогда, ну разве что если заниматься хацкелем серьёзно, то про расширяемые эффекты почитать стоит.

qnikst ★★★★★
()
Ответ на: комментарий от monk

Я там имел в виду C++/Java/...

плюсы я, к сожалению, не умею, а в яве можно так же извращаться сделав всякие Option<A> и Variant<A,B> какие сделаны в скале, но с одной стороны jit там ещё не научился такие штуки убирать (ладно, может и научился, но в 6-ке не умел), да и представление объектов такое, что будет лишний оверхед и со стиранием типов тоже может быть веселуха.

В хацкеле можно даже попытаться решать общий случай, но type-фу должно быть хотя бы 3 микроолега. Идея решения в том, что произвольный sum-type можно записать в виде HList, т.е. пишешь тип Maybe A :. Maybe B :. Maybe C и каким-нибудь чудом добавляешь констрент что Just только один из них. Или прящешь значение в экзестенциальный тип с констреинтом Typeable, и добавляешь typelevel list с перечислением всех возможных типов

  • , но это как-то все печально выглядит.

    Стоит сказать, что в если забить на вытаскивание всего в тайплевел, то через Typeable все сделается просто.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

доходчиво?

Да. Думал, будет сложнее.

send :: Socket -> Packet -> IO (Either Ok Reason)

Не так. Там либо возвращается Ok, либо пара {error, Reason}. То есть в хаскеле должно быть Ok | Error Reason

А в gen_tcp надо вернуть Ok Result | Error Reason.

В get_closest_pid --- Pid PidID | Error Reason.

Если я попытаюсь описать эти типы, Haskell начинает ругаться на неуникальность конструкторов.

С Maybe(Responce) аналогично. Вместо Ok Result | Error Reason | Nothing получаем Just Ok Result | Just Error Reason | Nothing. Ну ладно, опустим, живого примера, где было бы нужно Nothing без Maybe с ходу не придумывается.

В принципе, можно все выразить через Either / Maybe. Но тогда типы становятся невыразительными. Типа Either Result Reason. Не видно, что Reason будет в случае ошибки. Кроме того, реальные Result и Reason могут вообще совпасть (например, и там и там число), тогда без тэга совсем никак.

monk ★★★★★
() автор топика
Ответ на: комментарий от qnikst

Нафига лифтио-то? Оно же и так будет работать, не?

foo = do s <- getLine 
         if s == "ok"
         then refresh >> return (Just "ok") 
         else return Nothing 

anonymous
()
Ответ на: комментарий от monk

Начну с конца:

В принципе, можно все выразить через Either / Maybe. Но тогда типы становятся невыразительными. Типа Either Result Reason. Не видно, что Reason будет в случае ошибки. Кроме того, реальные Result и Reason могут вообще совпасть (например, и там и там число), тогда без тэга совсем никак.

на самом деле видно, т.к. это следует из семантики either, (в целом это есть во всех книгах документации и haskell report)

data Either a b = Left a | Right b

Обычная семантика Left - это ошибка / остановка вычисления, Right - значение, но можно рассматривать и как sum type. Т.е.

instance Monad (Either a) where
  (Left x) >>= g = Left x
  (Right x) >>= g = g x

На самом деле достаточно даже посмотреть самый простой контекст - функтор:

instance Functor (Either a) where
  fmap f (Right x) = Right (f x)
  fmap _ l = l

тут видно, что «а» «фиксирован, и 'Either a' параметризуется типом 'b'.

(из этого следует, что я должен был нарисать Either Reason Ok, но поспешил)

давай теперь снова рассмотрим типы. Мне лень перепечатывать несколько раз, так бы объяснение было более верным, поэтому я сразу замечу, что Reason во всех типах на самом деле это одно и тоже, поэтому я могу предположить, что мы ввели тип

data Reason = Reason ...

(возможно это может быть просто тип-обёртка newtype или даже синоним типа type, но пока предположим, что это что-то сложное со своей структурой, общности это не снижает). Если бы я этого не „заметил“ то я бы сначала ввел много разных Reason а потом показал бы, что это одно и тоже.

Там либо возвращается Ok, либо пара {error, Reason}. То есть в хаскеле должно быть Ok | Error Reason

т.е. тип data SendResponce = SendResponceOk | SendResponceError Reason, что тут важно: у каждого из варианта ADT есть свой конструктор, таким образом если я пишу SendResponceError Reason это однозначно определяет тот *Responce к которому ошибка относится.

Т.е. при записи если у нас есть множество ADT T_i, с конструкторами T^i_j, то \exists \unique T_i такой, что T^i_j \in T_i, т.е. если ты просто написал конструктор, то компилятору нужно понять, какой тип конструировать, поэтому он и ругался на одинаковые констукторы, о чем ты писал ниже (*).

Данный тип полностью изоморфен Either Reason ().

А в gen_tcp надо вернуть Ok Result | Error Reason.

Аналогично data GenTCPResponce = GenTCPOk GenTCPResponceResult | GenTCPERROR Reason, изоморфно Either Reason GenTCPResponceResult. Вот с GenTCPResponceResult уже нельзя сказать, что можно абстрагироваться, т.к. результат здесь вполне конкретный.

Если я попытаюсь описать эти типы, Haskell начинает ругаться на неуникальность конструкторов.

Чуть выше.

Теперь мы можем заметить интересную штуку, что все OK на самом деле одинаковые (на самом деле это не совсем так, но мы же смотрим семантику erlang), поэтому мы можем написать

data Ok a = Ok a

и если был просто Ok, то он становится Ok (). В данных функциях это не важно, а вот в целом в erlang часто шлются Ok и Ok Value, так что один тип и будет использоваться.

Про использование Either/Maybe или специфических типов, тут однозначного ответа нету, плюс специфических типов - то что они чуть более информативны, но если их семантика не отличается от Either/Maybe, то вторые лучше тем, что их семантику все знают и для них написано много библиотек.

Так же у любителя динамических языков может возникнуть вопрос, а какого черта нам нужны типы как теги?! Т.е. если у меня Reason один и тот же, то я вполне себе могу хотеть иметь

data A = A | Reason
data B = B | Reason

тут несколько ответов, во-первых данные это не просто struct, а к ним ещё могут „цепляться“ (в кавычках, т.к. зачастую это все сделается статически) словари, напр.

class Foo a where foo :: a -> Int
instance Foo A where foo _ = 1
instance Foo B where foo _ = 2

и вызываем foo Reason, что делать бедному компилятору?

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

data DynamicValue = forall a . Typeable a => DynamicValue a

т.е. мы говорим, что в DynamicValue лежит что-то, со словарём Typeable (кстати тут уже словарь таскается в runtime)

и потом при работе с данными интерпретатор пытается узнать, что там (на самом деле он тупо смотрит type у union описывающего значение, но так веселее)

inspect :: DynamicValue -> Maybe a
inspect (DynamicValue a) = cast a

если очень хочется, то в haskell можно пойти тем же путём :)

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

дык, всё действие в X должно быть, а не в IO

Ааа, там другая мондада. Понятно тогда.

anonymous
()
Ответ на: комментарий от qnikst

GenTCPResponceResult

Вот это грустно. Кончается чем-то вроде GtkTreeViewColumnFixedHeightFromFont

Там вроде пространства имён есть... Может как-то можно делать GenTCPResponce.Result с возможностью import GenTCPResponce as tcp ? Или только разбивая на файлы-модули?

monk ★★★★★
() автор топика
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

Только разбивая на файлы модули. Ну это слегка вырожденый случай, я подозреваю, что ответ это какой-нибудь ProcessName, где data ProcessName = P1 Pid | P2 Host Name | P3 Name, или какие там варианы в erlang.

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

data Person = Person {name :: String, ..}
            | VirtualPerson { name :: String, .. } -- можно

data Elf = Elf { name :: String, ..} -- уже нельзя

в итоге решают всякими префиксами, что выглядит убого. Есть правда https://ghc.haskell.org/trac/ghc/wiki/Records/OverloadedRecordFields но пока не смержили, обещались к 7.10..

qnikst ★★★★★
()
Последнее исправление: qnikst (всего исправлений: 1)
Ответ на: комментарий от qnikst

в итоге решают всякими префиксами, что выглядит убого.

Вот всё в Хаскеле хорошо. Кроме синтаксиса. Хотя, с другой стороны, язык развивается. Может допилят. Да и вариант «один тип - один модуль» тоже имеет право на жизнь.

monk ★★★★★
() автор топика

О! Лиспосрачи вернулись! :)

Zubok ★★★★★
()
Ответ на: комментарий от anonymous

но скажите, какая сейчас самая правильная scheme/lisp для встраивания в сишечку?

Спроси отдельной темой. Кто твой одинокий вопрос найдет на шестой странице между динамикой и хаскелем :)

antares0 ★★★★
()
Ответ на: комментарий от anonymous

самая правильная scheme/lisp для встраивания в сишечку?

Попробуй chibi-scheme

Puzan ★★★★★
()
Ответ на: комментарий от anonymous

Конечно нет. Если в динамике просто пишешь и - just works, то в статике надо еще повоевать с компилятором.

у тебя руки из жопы. А я с компилятором не воюю. А наоборот, компилятор мне помогает вылавливать ошибки и опечатки, когда я например случайно складываю апельсины + паровозы. А вот твой говноязычок берёт, и тупо складывает. И получается HEX.

Ни разу не кастовал строку в число.

да я тоже _не_ _хотел_ так кастовать. Оно само скастовалось, молча. В этом-то и беда.

Зачем ты рассуждаешь о том, чего не бывает?

блжад, не только «бывает», но и БЫВАЛО. Лично у меня. И такие ошибки очень сложно отыскать(потому что с точки зрения ЯП это фича, а не баг).

Мы же говорим о strong dynamic vs strong static - тут описанных тобой проблем не бывает.

что такое strong dynamic на практике?

Конечно, используешь. Тебе не приходится тратить время на исправление ошибок чекера и проставление типов - это уже использование, которое существенно ускоряет процесс разработки.

про чекер я уже говорил. Что касается типов, то их всё равно надо проставлять, что-бы потом поддерживать этот код. А то вот в говножабоскриптах я постоянно путаюсь, в попытках понять, ЧТО ЭТО? И да, написать код нужно всего один раз, и это не долго. А вот с поддержкой сложнее.

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

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

emulek
()
Ответ на: комментарий от monk

В статике ещё хуже. Есть переменная «доля чего-то в процентах». Тип должен быть 0..100.

твоя ошибка в самом начале. С какого перепугу тип должен быть целым? ДОЛЯ чего-то разве может быть ЦЕЛОЙ? Процент это по определению рациональная дробь, а значит для него тип должен быть RATIONAL или если ЯП так не умеет, то float/double (при этом надо помнить, что float/doouble это рациональная дробь с знаменателем 2^p, и что 100 не степень двойки, и значит будет погрешность, хоть и небольшая обычно).

В то время как очевидно, что для a = b = 60 уже нарушение контракта.

GIGO. 146% на выборах оттуда-же. И это уже не лечится никакими тестами.

Типы часто дают ложное ощущение

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

emulek
()
Ответ на: комментарий от emulek

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

А я пишу код осмысленно, не рандомно стуча по клавиатуре. По-этому никогда не складываю апельсины с паровзами и подобные примитивные гарантии мне неинтересны

да я тоже _не_ _хотел_ так кастовать. Оно само скастовалось, молча. В этом-то и беда.

Это слабая типизация. Слабая типизация - это зло, никто не спорит. Мы же обсуждаем сильную динамическую - там ничего ни к чему САМО МОЛЧА не кастуется.

что такое strong dynamic на практике?

Нету никаких неявных кастов типа строка к числу и т.п. изъебств из пехепе или жабаскрипта. Если у тебя строка - то это строка. И она не перестанет быть строкой, если ты только не применишь явно что-нибудь вроде string->integer и т.п.

ро чекер я уже говорил. Что касается типов, то их всё равно надо проставлять, что-бы потом поддерживать этот код. А то вот в говножабоскриптах я постоянно путаюсь, в попытках понять, ЧТО ЭТО? И да, написать код нужно всего один раз, и это не долго. А вот с поддержкой сложнее.

На это есть документация. Ну или контракты, тащем-то, которые значительно более выразительны, чем типы. Я в этом плане за soft typing, вообще.

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

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

anonymous
()
Ответ на: комментарий от emulek

твоя ошибка в самом начале. С какого перепугу тип должен быть целым? ДОЛЯ чего-то разве может быть ЦЕЛОЙ?

Он вроде никто не говорил про целое. Просто диапазон от 0 до 100. Ну и на самом деле не важно целое или нет, вопрос-то не в том

GIGO. 146% на выборах оттуда-же. И это уже не лечится никакими тестами.

Ну почему? Мой пример с контрактами для неправильной ф-и просто рандом-тестингом фальсифицирует. Так что все лечится даже не самописными тестами - а более примитивными инструментами

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

Ну на практике, если начать разбираться, что именно типы решают - то окажется что для большинства задач есть более хорошие инструменты, а те, на которых типы топ-1 просто не слишком существенны. При этом оверхед от типов ощутим.

anonymous
()
Ответ на: комментарий от anonymous

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

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

А вот килограммы можно очень долго складывать с долларами, и это никто не замечает(никому и в голову такое не придёт, что твоя программа так делает). Потом ВНЕЗАПНО это будет замечено, и кто будет покрывать убытки?

emulek
()
Ответ на: комментарий от monk

Если typedef int miles; typedef int percents; то не отследит.

потому что typedef определяет не тип, а псевдоним типа. Учи матчасть.

Или покажи кусок кода, как ты определяешь miles и percents.

сказали же:

class miles {};
emulek
()
Ответ на: комментарий от monk

что «предположим тип предметной области — доля чего-то. Тогда должно быть 0..100, но язык такое не позволяет»

ты точно упорот, если считаешь, что ДОЛЯ может быть ЦЕЛЫМ.

Или всё-же троллишь так?

emulek
()
Ответ на: комментарий от monk

Так можно утверждать, что наличие в русском языке окончаний и падежей заставляет высказывать верные утверждения (ибо часть неверных утверждений синтаксически некорректна :-)

не. Ты точно НЕ нормальный. Истинность утверждений от синтаксиса не зависит. Совсем косо и нелепо подменяешь понятия.

emulek
()
Ответ на: комментарий от anonymous

А есть хоть какаято причина писать декларации, если можно не писать?

во первых для того, что-бы знать, ЧТО это такое.

Или ты пишешь нечитаемый write-only говнокод?

emulek
()
Ответ на: комментарий от anonymous

А я пишу код осмысленно, не рандомно стуча по клавиатуре. По-этому никогда не складываю апельсины с паровзами и подобные примитивные гарантии мне неинтересны

ясно. Стало быть ничего длиннее 100 строк ты ещё не писал.

Нету никаких неявных кастов типа строка к числу и т.п. изъебств из пехепе или жабаскрипта. Если у тебя строка - то это строка. И она не перестанет быть строкой, если ты только не применишь явно что-нибудь вроде string->integer и т.п.

и где тут «dynamic»?

На это есть документация.

facepalm

emulek
()
Ответ на: комментарий от anonymous

Он вроде никто не говорил про целое. Просто диапазон от 0 до 100.

именно про INTEGER он и говорил почему-то. Почему я сам не понял. Но ему нужно было именно ЦЕЛОЕ от 0 до 100 включительно. Причём если смысл 100% для него очевиден, то смысл 120% почему-то нет. Я же предлагал float/double и следить за точностью. Или это не просто «процент», а какой-то конкретный процент чего-то. Тогда перед сложением 60%+60% следует сначала обосновать правомочность самого сложения. Как в C++ к примеру, определить метод operator+().

Ну и на самом деле не важно целое или нет, вопрос-то не в том

мы про типы, или где?

Ну почему? Мой пример с контрактами для неправильной ф-и просто рандом-тестингом фальсифицирует.

это как? Я что-то не понял.

При этом оверхед от типов ощутим.

нет никакого оверхеда от типов, только в очень простом и примитивном коде, который вычисляет например 2*2=? и даёт в ответе 4. В более сложном случае этот ответ не всегда правильный(сейчас опять меня начнут за GF ругать) и мало того, часто сам смысл операции под вопросом. Например указатели в сишке не то-что умножать, даже складывать UB.

emulek
()
Ответ на: комментарий от emulek

ты точно упорот, если считаешь, что ДОЛЯ может быть ЦЕЛЫМ.

А при чем тут целые?

anonymous
()
Ответ на: комментарий от emulek

ясно. Стало быть ничего длиннее 100 строк ты ещё не писал.

Нет, просто: «я пишу код осмысленно, не рандомно стуча по клавиатуре.». В этом смысле сложить жирафов с носорогами принципиально невозможно. Если же ты пишешь код фейсролом - то там офк и не только жирафы с носорогами могут получиться.

и где тут «dynamic»?

Везде. Ты определяются в рантайме. Это strong dynamic.

facepalm

Типы документацию не заменяют. На практике, кстати, говорящие названия аргументов гораздо лучше документируют код, чем аннотации типов.

anonymous
()
Ответ на: комментарий от emulek

именно про INTEGER он и говорил почему-то.

Не помню что он говорил про integer. Ну да не важно, это ничего не меняет. Смысл в том, чтобы ввести ограничение на тип.

Я же предлагал float/double и следить за точностью.

Ну пусть у тебя будет float/double, как ты гарантируешь, что оно не станет больше 100?

мы про типы, или где?

Про типы. Просто ты не внимательно читал тред. Замена integer на float тут ничего не меняет. Вопрос был - как на статическом уровне гарантировать отсутствие выхода за диапазон.

это как? Я что-то не понял.

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

нет никакого оверхеда от типов, только в очень простом и примитивном коде, который вычисляет например 2*2=? и даёт в ответе 4.

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

anonymous
()
Ответ на: комментарий от lovesan

Вот тебя-то и не хватало, лол

Только хватку что-то теряешь, слабо выступил

anonymous
()
Ответ на: комментарий от mv

чем несохранённый перед коммитом емаксовский буфер.

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

вообще, я кстати так и не нашел в нем некоторых вещей, типа checkout single file какой-то версии.. пользуюсь git-timemachine

zarkone ★★
()
Ответ на: комментарий от anonymous

Ну на практике, если начать разбираться, что именно типы решают - то окажется что для большинства задач есть более хорошие инструменты, а те, на которых типы топ-1 просто не слишком существенны. При этом оверхед от типов ощутим.

+1 точно в яблочко

Ritmik
()
Ответ на: комментарий от mv

я так и подумал — в основном тоже из консоли, но действия типа push/commit/log все-таки в Emacs — удобнее.

zarkone ★★
()
Ответ на: комментарий от monk

Racket + Typed Racket идеальны за исключением производительности при взаимодействии.

Мне racket тоже нравится. Но я всё-таки предпочитаю статическую типизацию, поэтому пробовал typed racket. Вот только когда пытался использовать постоянно натыкался на вещи, которые «ещё не реализованы» в типизированном диалекте.

Кстати, обьявлять константы в ракете ведь нельзя?

DarkEld3r ★★★★★
()
Ответ на: комментарий от Ritmik

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

qnikst ★★★★★
()
Ответ на: комментарий от DarkEld3r

Кстати, обьявлять константы в ракете ведь нельзя?

Объяви переменную в своем модуле (лучше с говорящим (что это константа) именем) и не меняй ее.

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

x4DA ★★★★★
()
Ответ на: комментарий от x4DA

Объяви переменную в своем модуле (лучше с говорящим (что это константа) именем) и не меняй ее.

Да мне хотелось бы, чтобы и я внутри модуля менять не мог. Понятно, что есть соглашения об именовании, да и не так уж сложно «просто не менять», но хотелось бы гарантированного контроля.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

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

x4DA ★★★★★
()
Ответ на: комментарий от anonymous

Про константы: http://stackoverflow.com/a/6524488/1646982

Видел когда пытался сам найти решение. Особо изящным его не назовёшь... да и предложенное «решение» будет выглядеть ещё более уродливым в типизированном варианте.

DarkEld3r ★★★★★
()
Ответ на: комментарий от x4DA

Что у них полиморфные функции как аргументы не работают? Может быть, конечно. Мне там регистрироваться нужно еще. Так не хочется.

anonymous
()
Ответ на: комментарий от anonymous

Нашел фичу

Это правильная фича. Если ты передаёшь полиморфную функцию в полиморфную и вывести тип нельзя, то обязан указать какой именно тип в данном случае применять (инстанцировать):

> (map (inst id Any) '(1 2 3))
- : (Listof Any) [more precisely: (Pairof Any (Listof Any))]
'(1 2 3)

> (map (inst id Number) '(1 2 3))
- : (Listof Number) [more precisely: (Pairof Number (Listof Number))]
'(1 2 3)

Кстати, а Хаскелл в такой ситуации что делает?

monk ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.