Здравствуйте, vdimas, Вы писали:
V>Динамическое приведение типов в CLR (не преобразование, а именно приведение без изменения значения) — суть рантайм-тест этой "разметки", матч по алгебраическим типам в хаскеле — аналогично.
Ага. Вот только к динамической типизации это отношения не имеет. Ведь все типы известны во время компиляции.
АлгТД — это вид полиморфизма. Эдакое безопасное объедение.
Никто же не называет С++ динамически-типизированным только из того факта, что в нем поддерживаются виртуальные методы которые пиводят к тому, что реальный тип объекта становится известным только во время выполнения?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Ага. Вот только к динамической типизации это отношения не имеет. Ведь все типы известны во время компиляции.
Какие еще все? Все, входящие в алг. тип? Дык их несколько в общем случае, а конкретный мы определяем в рантайм.
VD>Никто же не называет С++ динамически-типизированным только из того факта, что в нем поддерживаются виртуальные методы которые пиводят к тому, что реальный тип объекта становится известным только во время выполнения?
В случае виртуальных методов, если я правильно понял о чем ты, мы и не должны динамически определять тип реального объекта, такой подход — следствие правила Лисков.
Тут ведь различие очевидное: при динамическом приведении типа мы должны привести к некоему типу (неважно, матчингом или оператором динамического приведения типа) ДО выполнения операций над типом. В условиях открытой группы типов (так можно назвать все доступные типы) — это признанно плохим тоном в программировании. К сожалению, безопасность такого преобразования в CLR провоцирует подобный подход. С другой стороны, на динамическом приведении типов гораздо проще решаются многие задачи (та же обработка AST), т.к. в этих случаях нам доступен более подходящий данной задаче способ декомпозиции функциональности, в сравнении с размазыванием оной по виртуальным ф-иям многих классов. Отсюда я и считаю, что для тех задач, где сегодня мы используем динамическое приведение типов (даже если оно безопасное с т.з. возможной неверной интерпретации памяти), алгебраические типы будут более удобны, и более безопасны, но уже на уровень выше — в алгоритмическом плане, преодолевая то самое правило Лисков в ввиду закрытости группы (но опять же, только в случае контроля компилятором полноты матчинга, иначе та самая "дыра" остаётся в первозданном виде).
Здравствуйте, vdimas, Вы писали:
V>...Отсюда я и считаю, что для тех задач, где сегодня мы используем динамическое приведение типов (даже если оно безопасное с т.з. возможной неверной интерпретации памяти), алгебраические типы будут более удобны, и более безопасны, но уже на уровень выше — в алгоритмическом плане, преодолевая то самое правило Лисков в ввиду закрытости группы (но опять же, только в случае контроля компилятором полноты матчинга, иначе та самая "дыра" остаётся в первозданном виде).
Я так и не понял какое отношения все эти рассуждения имеют к динамической типизации в стаически-типизированных языках?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
В качестве резюме дискуссии, если её ещё кто-то читает:
идём читать The Haskell 98 Report по ссылке и находим 3.13 Case-выражения,
чуть дальше читаем
Case-выражение должно иметь по крайней мере одну альтернативу, и каждая альтернатива должна иметь по крайней мере одно тело.
Каждое тело должно иметь один и тот же тип, и все выражение должно быть того же типа.
Если сомнения ещё остались, то можно прочесть 3.17 Сопоставление с образцом:
Образцы появляются в лямбда-абстракциях, определениях функций, связываниях с образцом, описаниях списков, do-выражениях и case-выражениях. Тем не менее, первые пять из них в конечном счете транслируются в case-выражения, поэтому достаточно ограничиться определением семантики сопоставления с образцом для case-выражений.
now playing: Dubfire — I Feel Speed (Alternative Mix)
Здравствуйте, EvilChild, Вы писали:
EC>В качестве резюме дискуссии, если её ещё кто-то читает: EC>идём читать The Haskell 98 Report по ссылке и находим 3.13 Case-выражения, EC>чуть дальше читаем EC>
EC>Case-выражение должно иметь по крайней мере одну альтернативу, и каждая альтернатива должна иметь по крайней мере одно тело.
EC>Каждое тело должно иметь один и тот же тип, и все выражение должно быть того же типа.
Опять у кого-то кое-где каша.
Каждое тело должно иметь один и тот же тип, т.к. это будет тип всего case-выражения, но этот тип может быть произвольным, не связанным с аргументом case.
Так что не вижу тут никакого резюме, даже не вижу связи с обсуждаемым.
Здравствуйте, vdimas, Вы писали:
EC>>В качестве резюме дискуссии, если её ещё кто-то читает: EC>>идём читать The Haskell 98 Report по ссылке и находим 3.13 Case-выражения, EC>>чуть дальше читаем EC>>
EC>>Case-выражение должно иметь по крайней мере одну альтернативу, и каждая альтернатива должна иметь по крайней мере одно тело.
EC>>Каждое тело должно иметь один и тот же тип, и все выражение должно быть того же типа.
V>Опять у кого-то кое-где каша. V>Каждое тело должно иметь один и тот же тип, т.к. это будет тип всего case-выражения, но этот тип может быть произвольным, не связанным с аргументом case.
V>Так что не вижу тут никакого резюме, даже не вижу связи с обсуждаемым.
Ок, ваш слив засчитан.
now playing: Dubfire — I Feel Speed (Alternative Mix)
Здравствуйте, vdimas, Вы писали:
V>Обобщенный тип для алгебраического типа — это не аналог параметризуемого обобщенного типа из С++, это аналог union из того же С++. Это некий bag, куда заворачиваются другие типы, составляющие группу.
V>В общем, типы, входящие в группу, в общем случае не приводимы один к одному, поэтому нужен механизм, чтобы в одном аргументе передавать значения разных типов. Этот механизм — алгебраические типы. Типы заворачиваются в discriminated union.
EC>>Если добавить аргументов конструкторам значений, то с точки зрения типов ничего не изменится.
V>Ты бы сначала добавил.
<...>
EC>>Я не понимаю почему ты не хочешь провести границу между абстракцией и её реализацией.
V>А я не понимаю твоего упорства — всё более чем прозрачно.
V>И как раз об абстракции и говорил в первом утверждении, что полиморфизм по алгебраическим типам — это классика динамической типизации. Могу усилить — это наилучший на сегодня способ использования динамической типизации.
Совсем не обязательно реализовывать алгебраические типы через группировку других типов и т.п.
//data AAA = X Int Int | Y Int Int Intpublic class AAA
{
private readonly int[] data;
private enum Discr { X, Y }
private readonly Discr discr;
private AAA(Discr discr, int[] data)
{
this.data = data;
this.discr = discr;
}
public static AAA CreateX(int a1, int a2)
{
return new AAA(Discr.X, new[] { a1, a2 });
}
public static AAA CreateY(int a1, int a2, int a3)
{
return new AAA(Discr.Y, new[] { a1, a2, a3 });
}
private MatchResult<T> matchX<T>(Func<int, int, T> f)
{
if (discr != Discr.X)
return MatchResult<T>.NotMatch;
return new MatchResult<T>(f(data[0], data[1]));
}
private MatchResult<T> matchY<T>(Func<int, int, int, T> f)
{
if (discr != Discr.Y)
return MatchResult<T>.NotMatch;
return new MatchResult<T>(f(data[0], data[1], data[2]));
}
//Принимает параметрами функции для всех возможных вариантовpublic T Match<T>(Func<int, int, T> f_X, Func<int, int, int, T> f_Y)
{
var result = matchX(f_X);
if (result != MatchResult<T>.NotMatch)
return result.Result;
return matchY(f_Y).Result;
}
}
public class MatchResult<T>
{
public readonly bool IsMatch;
public readonly T Result;
public MatchResult(T result)
{
IsMatch = true;
Result = result;
}
private MatchResult(){IsMatch = false;}
public static readonly MatchResult<T> NotMatch = new MatchResult<T>();
}
//case myAAA of
// | X x y -> x + y
// | Y a b c -> a + b + cvar myAAA = AAA.CreateY(1, 2, 3);
var res = myAAA.Match((x, y) => x + y,
(a, b, c) => a + b + c);
Здравствуйте, EvilChild, Вы писали:
V>>Так что не вижу тут никакого резюме, даже не вижу связи с обсуждаемым.
EC>Ок, ваш слив засчитан.
Похоже, ты не понимаешь, почему в ФЯ выражение должно обязательно иметь тип, и почему для тел case этот тип должен быть одинаков, при этом вовсе не обязан быть алгебраическим.
Здравствуйте, artelk, Вы писали:
A>Совсем не обязательно реализовывать алгебраические типы через группировку других типов и т.п.
...
В этом примере ты всего лишь разместил по одному адресу значения для случаев X и Y, потому как это оказалось возможным с т.з. системы типов CLR (тип массива не зависит от последней размерности). Если бы зависело, как в С++, то так просто не вышло бы. А в рассматриваемых мною исходниках Хаскеля по одному адресу размещаются значения вообще различных типов, сразу после дискриминатора, такое размещение аналогично union из С/С++. Но в отличии от последнего, язык не предоставляет ср-ва для потенциально неверной реинтерпретации памяти.
Кстати, не столь важный, но все же существующий на сегодня момент ты подметил — оно неплохо смотрится для всяких PDU, мы поступаем аналогично при проектировании прикладных протоколов. Вернее, не совсем так же, но в общем случае стараемся переиспользовать поля такой самописной алгебраической структуры для различных значений дискриминатора (всё-равно ручками пока что долбим эти вещи).
По остальному:
— приватный конструктор и вмеcто него "статические конструкторы" — абсолютно правильно, однако мы часто делаем их как value type, когда по сценарию их множества хранятся, что оставляет некую "дырочку" в виде конструктора по-умолчанию.
— матч перемудрил, там просто case по дискриминатору надо сделать, безо всяких MatchResult, matchX и matchY.
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, artelk, Вы писали:
A>>Совсем не обязательно реализовывать алгебраические типы через группировку других типов и т.п.
V>...
V>В этом примере ты всего лишь разместил по одному адресу значения для случаев X и Y, потому как это оказалось возможным с т.з. системы типов CLR (тип массива не зависит от последней размерности). Если бы зависело, как в С++, то так просто не вышло бы. А в рассматриваемых мною исходниках Хаскеля по одному адресу размещаются значения вообще различных типов, сразу после дискриминатора, такое размещение аналогично union из С/С++. Но в отличии от последнего, язык не предоставляет ср-ва для потенциально неверной реинтерпретации памяти.
Типы параметров конструкторов X и Y не обязаны быть одинаковыми (Int из примера). В плюсах было бы что-нибудь вроде "char* data = new char[size]" и интерпретацией памати "(int*)(data+2)", правда со всякими выравниваниями пришлось бы бороться, похоже.
Только Хаскель ведь не в C++ транслируется перед компиляцией. Если б транслировался, тогда да, проще всего было бы сделать через union, но это было бы внутренней деталью реализации компилятора, а не чем-то, относящесмя к самому Хаскелю.
Вся интерпретация памяти жестко инкапсулирована. Доступ к полям осуществляется только по матчу — путем передачи лямбд для каждого варианта. Runtime ошибок здесь быть не может вообще.
V>- матч перемудрил, там просто case по дискриминатору надо сделать, безо всяких MatchResult, matchX и matchY.
Ага, можно въинлайнить matchX и matchY в Match и избавиться от MatchResult. Просто хотелось акцентировать на том, что matchX и matchY по отдельности — private методы
V>Похоже, ты не понимаешь, почему в ФЯ выражение должно обязательно иметь тип, и почему для тел case этот тип должен быть одинаков, при этом вовсе не обязан быть алгебраическим. V>
V>bool2int Bool a = case a of
V> True -> 1
V> False -> 0
V>
Я не понимаю какое отношение то, что ты пишешь имеет к обоснованию динамической типизации паттерн матчинга в хаскеле.
Ты можешь дать ссылку на место в The Haskell 98 report подтверждающее твои слова?
А то надоело слушать отвлечённые рассуждения о том как что-то можно сделать в C++ или поверх CLR.
Здравствуйте, VladD2, Вы писали:
VD>Но такая фича не поддерживается в дотнете. По этому даже ФЯ реализованные на дотнете не поддерживают записей (по крайней мере в полной мере).
В скале эта фича есть, скала может компилироваться в .NET. Проблема выруливается на этапе компиляции преобразованием (1, "1") в new Tuple2<Int,String> (1, "1"). На данный момент поддерживаются до Tuple22.
Здравствуйте, gandjustas, Вы писали:
A>>Анонимные типы, очень хорошая аналогия, только пользы от них в данном случае никакой нет. Надо уметь возвращать вполне конкретные типы, а какие именно автоматически не вывести. G>Это решется с помощью метаданных. Причем метаданные могут быть выведены автоматически (следующая версия EF это будет уметь)
Имеется в виду что-то типа следующего?
$ scala
Welcome to Scala version 2.7.4.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_13).
Type in expressions to have them evaluated.
Type :help for more information.
scala> case class User(id: Int, firstName: String, lastName: String) {
| def fullName = firstName + " " + lastName
| }
defined class User
scala> val tuple = (1, "John", "Brown")
tuple: (Int, java.lang.String, java.lang.String) = (1,John,Brown)
scala> implicit def tuple2user(t: Tuple3[Int, String, String]) = new User(t._1, t._2, t._3)
tuple2user: ((Int, String, String))User
scala> tuple.fullName
res0: java.lang.String = John Brown
Здесь преобразование выполняется не совсем автоматически: явно заданным implicit-методом (вполне, впрочем, простым).
Здравствуйте, dimgel, Вы писали:
VD>>Но такая фича не поддерживается в дотнете. По этому даже ФЯ реализованные на дотнете не поддерживают записей (по крайней мере в полной мере).
D>В скале эта фича есть, скала может компилироваться в .NET. Проблема выруливается на этапе компиляции преобразованием (1, "1") в new Tuple2<Int,String> (1, "1"). На данный момент поддерживаются до Tuple22.
Tuple — это кортеж. Речь же шла о записи — record. Запись это, можно сказать, кортеж с именованными полями.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Tom>Очень интересно Ваше мнение на тему сабсоника.
Какое может быть мнение?
Я этим не пользовался, так что ничего особо сказать не могу.
На первый взгляд выглядит фигней, так как то и дело встречаются строковые константы (стало быть о проверке типов во время компиляции и интеллисенсе речи идти не может).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Tom>>Очень интересно Ваше мнение на тему сабсоника.
VD>Какое может быть мнение? VD>Я этим не пользовался, так что ничего особо сказать не могу.
VD>На первый взгляд выглядит фигней, так как то и дело встречаются строковые константы (стало быть о проверке типов во время компиляции и интеллисенсе речи идти не может).
Строковые константы там только для того что бы показать что и так можно. Более того, если есть строка (... SET A = 'B'), то сабсоник распарсит такую конструкцию и в итоге передаст 'B' как параметр.
Предлагаю обсуждать в том топике, этот уже нездорово большой
Здравствуйте, Tom, Вы писали:
Tom>Более того, если есть строка (... SET A = 'B'), то сабсоник распарсит такую конструкцию и в итоге передаст 'B' как параметр. Tom>Предлагаю обсуждать в том топике, этот уже нездорово большой
Когда распарсит?
Из примеров я вижу, что все откладывается на рантайм.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, IT, Вы писали:
IT>Кстати, забыл про SQL отдельно от кода. Иногда приложение можно существенно оптимизировать, генерируя и оптимизируя код SQL на клиенте. За примером далеко ходить не надо — это наш сайт и конкретно MainList.aspx. Переписывание кода со статического SQL на Linq2SQL решило проблему, над которой мы бились несколько лет, пытаясь тщетно подкрутить монстрообразный запрос индексами и хинтами.
покажите в образовательных целях что было и что стало, а то пример проскакивает неоднократно, но ненаглядно.
Здравствуйте, ili, Вы писали:
ili>покажите в образовательных целях что было и что стало, а то пример проскакивает неоднократно, но ненаглядно.
[SqlQuery(@"
WITH Numbered AS
(
SELECT TOP (@startRecord + @maxRecords)
ROW_NUMBER() OVER (ORDER BY fm.updt DESC) RowNum,
fm.mid ID_Message,
fm.subj Subject,
fm.uid ID_User,
fm.usernick UserNick,
fm.nans AnswerCount,
fm.updt UpdateTime,
fm.lastone LastMessageAuthor,
fm.gid ID_Forum,
fg.ref ForumShortName,
(SELECT sum(Rating) FROM MessageRating WHERE ID_Topic = fm.mid) Rating,
(SELECT Count(*) FROM self WHERE mid = fm.mid AND (dte > COALESCE(fm.last_moderated,0))) SelfCount,
fm.dte [Date]
FROM forum_tree fm
INNER JOIN AllowedReadGroups(@userID,0) fg ON fg.gid = fm.gid
WHERE fm.tid=0
AND ((@nans IS NULL) OR (nans = @nans))
AND NOT EXISTS(SELECT 1 FROM forum_filter ff WHERE ff.gid=fm.gid AND uid=@userID)
ORDER BY fm.updt DESC
)
SELECT * FROM Numbered WHERE RowNum BETWEEN @startRecord + 1 AND @startRecord + @maxRecords
OPTION (MAXDOP 1)")]
[DataSetTable("Message")]
public abstract ForumDataSet GetAllPublicTopics(
int userID,
[ParamName("nans"), ParamNullValue(-1)] int nAnswers,
int startRecord, int maxRecords);
public virtual ICollection GetAllPublicTopicsEx(UserInfo user, IEnumerable<int> allowed, IEnumerable<int> skip, int nAnswers, int startRecord, int maxRecords)
{
using (var db = RsdnDataContext.Create())
{
var ucread = user.IsAnonym? UserRole.User: user.UserRole;
var result =
from mt in db.MessageTrees
join f in db.Forums on mt.ForumID equals f.ForumID
where
mt.TopicID == 0 &&
f.ForumID > 0 &&
(f.UserReadLevel >= (short)ucread || allowed.Contains(f.ForumID)) &&
!skip.Contains(f.ForumID)
orderby mt.UpdatedOn descending
select new
{
ID_Message = mt.MessageID,
Subject = mt.Subject,
ID_User = mt.UserID,
UserNick = mt.UserNick,
AnswerCount = mt.AnswerNumber,
UpdateTime = mt.UpdatedOn,
LastMessageAuthor = mt.LastAnswerBy,
ID_Forum = mt.ForumID,
ForumShortName = f.Alias,
UserRole = f.UserReadLevel,
Rating = (from mr in db.MessageRatings where mr.ID_Topic == mt.MessageID select mr.Rating).Sum()
};
if (nAnswers >= 0)
result = from r in result where r.AnswerCount == nAnswers select r;
result = result.Skip(startRecord).Take(maxRecords);
return result.ToList();
}
}
Первый метод как было, второй как стало.
В 95% работают следующие оптимизации:
— allowed.Contains устраняется,
— пейджинг превращается в TOP,
— проверка AnswerCount не делается.
Если нам не помогут, то мы тоже никого не пощадим.