Juanito Написано Децембар 29, 2014 Аутор Пријави Подели Написано Децембар 29, 2014 Не, не бих то. T и U нису нужно никако повезани (може да буде U : T, али не мора), осим преко Func<T, U>. Оно што бих желео је да натерам Map да врати исти конкретни тип који га имплементира. На пример, желео бих да ово буде увек compile time error: class SomeList<T> : IMappable<T> { SomeArray<T> Map<U>(Func<T, U> mappingFuntion) { // bla bla } } А да само ово буде тачно: class SomeList<T> : IMappable<T> { SomeList<T> Map<U>(Func<T, U> mappingFuntion) { // bla bla } } Хипотетичка декларација жељеног интерфејса би изгледала некако овако: interface IMappable<T> { This<U> map<U>(Func<T, U> mappingFunction); } где This означава тај исти конкретни тип, а не било које IMappable<T>. Grizzly Adams је реаговао/ла на ово 1 Link to comment Подели на овим сајтовима More sharing options...
Grizzly Adams Написано Децембар 29, 2014 Пријави Подели Написано Децембар 29, 2014 Добра идеја, али мислим да тако нешто не постоји. Јави Хајлсбергу... ΜΟΛΩΝ ΛΑΒΕ Link to comment Подели на овим сајтовима More sharing options...
Grizzly Adams Написано Децембар 29, 2014 Пријави Подели Написано Децембар 29, 2014 One reason why people want to do this is to enforce a particular constraint in a type hierarchy. Suppose we have abstract class Animal { public virtual void MakeFriends(Animal animal); } But that means that a Cat can make friends with a Dog, and that would be a crisis of Biblical proportions! (***) What we want to say is abstract class Animal { public virtual void MakeFriends(THISTYPE animal); } so that when Cat overrides MakeFriends, it can only override it with Cat. Now, that immediately presents a problem in that we've just violated the Liskov Substitution Principle. We can no longer call a method on a variable of the abstract base type and have any confidence that type safety is maintained. Variance on formal parameter types has to be contravariance, not covariance, for it to be typesafe. And moreover, we simply don't have that feature in the CLR type system. http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx Juanito је реаговао/ла на ово 1 ΜΟΛΩΝ ΛΑΒΕ Link to comment Подели на овим сајтовима More sharing options...
Grizzly Adams Написано Децембар 29, 2014 Пријави Подели Написано Децембар 29, 2014 Ви`ш уопште ми није пало на памет да у самој контрацепцији постоји проблем... Зарђавам полако... Juanito је реаговао/ла на ово 1 ΜΟΛΩΝ ΛΑΒΕ Link to comment Подели на овим сајтовима More sharing options...
Juanito Написано Децембар 29, 2014 Аутор Пријави Подели Написано Децембар 29, 2014 Добра идеја, али мислим да тако нешто не постоји. Јави Хајлсбергу... Haskell то може да изрази овако*: class Mappable f where fmap :: (a -> b) -> f a -> f b Ово class није исто што и OO class, већ више интерфејс на стероидима и назива се класом типова. И онда List, ако жели да буде Mappable, уради овако нешто: instance Mappable [] where fmap = map A map је исто што и Select map :: (a -> b) -> [a] -> [b] map _ [] = [] map f (x:xs) = f x : map f xs Haskell нема наслеђивање, па нема никакве могућности да се посвађамо са госпођом Лисков. *Само што луђаци тај ”интерфејс” не зову Mappable, него Functor. Прецизније, у питању је ендофунктор. Grizzly Adams је реаговао/ла на ово 1 Link to comment Подели на овим сајтовима More sharing options...
Juanito Написано Децембар 29, 2014 Аутор Пријави Подели Написано Децембар 29, 2014 Иначе, Swift дозвољава да метод декларисан у интерфејсу врати Self. Написаћу, кад стигнем, како избегавају проблем са наслеђивањем. Насумични пример из стандардне библиотеке (protocol je исто што и интерфејс): protocol _Incrementable : Equatable { /// Return the next consecutive value in a discrete sequence of /// `Self` values /// /// Requires: `self` has a well-defined successor. func successor() -> Self } Link to comment Подели на овим сајтовима More sharing options...
Juanito Написано Јануар 6, 2015 Аутор Пријави Подели Написано Јануар 6, 2015 F# open Microsoft.FSharp.Data.UnitSystems.SI.UnitNames open Microsoft.FSharp.Data.UnitSystems.SI.UnitSymbols let speed = 10<m/s> let time = 35<s> let distance = speed * time // val speed : int<Data.UnitSystems.SI.UnitSymbols.m/Data.UnitSystems.SI.UnitSymbols.s> = 10 // val time : int<Data.UnitSystems.SI.UnitSymbols.s> = 35 // val distance : int<Data.UnitSystems.SI.UnitSymbols.m> = 350 let speed = 10<m/s> let time = 35<s> let nonsense = speed + time // error FS0001: The unit of measure 's' does not match the unit of measure 'm/s' [<Measure>] type baba [<Measure>] type žaba let numberOfBabasInChurch = 5<baba> let numberOfBabasInPark = 7<baba> let numberOfŽabasInMorava = 9<žaba> let numberOfŽabasInDanube = 8<žaba> let totalNumberOfBabas = numberOfBabasInChurch + numberOfBabasInPark let totalNumberOfŽabas = numberOfŽabasInMorava + numberOfŽabasInDanube // val numberOfBabasInChurch : int<baba> = 5 // val numberOfBabasInPark : int<baba> = 7 // val numberOfŽabasInMorava : int<žaba> = 9 // val numberOfŽabasInDanube : int<žaba> = 8 // val totalNumberOfBabas : int<baba> = 12 // val totalNumberOfŽabas : int<žaba> = 17 let mixingGrandmothersAndFrogs = numberOfBabasInChurch + numberOfŽabasInMorava // error FS0001: The unit of measure 'žaba' does not match the unit of measure 'baba' Grizzly Adams and verum est in beer је реаговао/ла на ово 2 Link to comment Подели на овим сајтовима More sharing options...
Juanito Написано Јануар 11, 2015 Аутор Пријави Подели Написано Јануар 11, 2015 Microsoft koristi Haskell za bond, james bond kompajler https://github.com/Microsoft/bond/tree/master/compiler Link to comment Подели на овим сајтовима More sharing options...
Juanito Написано Јануар 11, 2015 Аутор Пријави Подели Написано Јануар 11, 2015 Честа техника у функционалном програмирању је да се од неке генералне функције вишег реда праве специјализоване једноставније функције. Једна од нарочито погодних за ову работу је функција која се у програмским језицима најчешће назива fold или reduce. Та функција је у ствари апстракција и генерализација следећег процедуралног кода: var result = initial for element in array { result = combine(result, element) } return result Swift STL има глобалну reduce функцију, која ради за било коју секвенцу, а такође има и метод reduce, дефинисан на типу Array: let sum = [1, 4, 8].reduce(0, combine: +) let product = [1, 4, 8].reduce(1, combine: *) Понекад је згодније дефинисати специјализоване функције, уместо директног коришћења reduce: func sum(array: [Int]) -> Int { return array.reduce(0, combine: +) } func product(array: [Int]) -> Int { return array.reduce(1, combine: *) } Производ и сума су лаки за разумевање, те ово и није баш преко потребно, али замислите случај у коме се reduce користи да се низ трансформише у хаш(иш) мапу, користећи неку компликовану операцију над елементима низа. Тада reduce може да постане помало нечитљива ако се директно користи и боље ју је увити у другу функцију описнијег имена и сакрити компликовану имплементацију. У овом посту сам писао о томе како су све функције у F# заправо функције које узимају један аргумент (currying и парцијална апликација). Сада можемо да видимо како је то корисно у пракси. Претходни Swfit код се у F# може написати на следећи начин: let sum = List.fold (+) 0 let product = List.fold (*) 1 let testSum = sum [1; 4; 8] // 13 let testProduct = product [1; 4; 8] // 32 Како ово ради? Потпис од List.fold је следећи: ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a List.fold узима функцију за комбиновање, почетну вредност и листу и враћа крајњи резултат. Али шта се дешава ако тој функцији само дамо функцију за комбиновање и почетну вредност? Па добићемо функцију која узима листу и враћа крајњи резултат. Е управо тако смо на изузетно елегантан начин написали sum и product. Можемо да се играмо на разне начине: let allTrue = List.fold (&&) true let anyTrue = List.fold (||) false let testAllTrue1 = allTrue [true; true; true] // true let testAllTrue2 = allTrue [true; false; true] // false let testAnyTrue1 = anyTrue [false; true; false] // true let testAnyTrue2 = anyTrue [false; false; false] // false Да ли можемо овако елегантни да будемо и у Swift-у. Можемо, али за ту елеганцију не можемо да искористимо уграђене reduce функције и методе. Зашто? Погледајмо потпис глобалне: func reduce<S : SequenceType, U>(sequence: S, initial: U, combine: (U, S.Generator.Element) -> U) -> U Имамо два проблема. Прво, ова функција није написана тако да омогућава парцијалну апликацију. То бисмо још и могли да решимо тако што ћемо да је трансформишемо ручно: func curry<A, B, C, D>(f: (a: A, b:B, c: C) -> D) -> (A -> (B -> (C -> D))) { return { a in { b in { c in f(a: a, b: b, c: c) } } } } // и онда користимо curry(reduce) уместо reduce Проблем је у томе што је, за разлику од List.fold из F#, први аргумент од reduce секвенца, други почетна вредност, а трећи функција комбиновања. Ово има и те како смисла у Swift-u, јер постоји погодна синтакса за коришћење ламбда израза, када је функција последњи аргумент друге функције. Нажалост, то нам одузима могућност да елегантно изводимо специјализоване функције из reduce. Да бисмо то могли да чинимо, морамо да напишемо погоднију reduce функцију. Назовимо је fold, да бисмо избегли конфликт имена, и напишимо је за низ уместо за секвенцу, ради једноставности: func fold<T, U>(initial: U) (combine: (U, T) -> U) (array: [T]) -> U { var result = initial for element in array { result = combine(result, element) } return result } Приметите како сам листу аргумената написао као (A)(B)© уместо (A, B, C). На тај начин функцију можемо парцијално применити, без потребе за ручним коришћењем горе дефинисане функције curry. За разлику од F#, где је currying default, у Swift-у је остављено као могућност. Сада можемо да напишемо нашу суму и производ: let sum = fold(0)(+) let product = fold(1)(*) Остале две функције, allTrue и anytrue, морају да буду за нијансу компликованије написане, због начина на кога су логички оператори дефинисани у STL: func &&<T : BooleanType>(lhs: T, rhs: @autoclosure () -> Bool) -> Bool func ||<T : BooleanType>(lhs: T, rhs: @autoclosure () -> Bool) -> Bool @autoclosure, како му само име каже, дату вредност аутоматски трансформише у closure, тј. функцију коју је могуће позвати касније, те омогућава одлагање израчунавања израза, што у овом случају омогућава short-circuiting. Aко је lhs нетачно, && ће одмах вратити нетачно, а ако је lhs тачно, || ће одмах вратити тачно, игноришући rhs. Ово је јако корисно, али због такве специфичне имплементације у овом случају не можемо нашој fold функцији да проследимо само && односно II, јер фолд очекује функцију типа (A, B) -> A, а не (A, @autoclosure () -> B) -> A. Мораћемо да користимо ламбда израз и да експлицитно дефинишемо тип од allTrue и anyTrue: let allTrue: [Bool] -> Bool = fold(true)({ $0 && $1 }) let anyTrue: [Bool] -> Bool = fold(false)({ $0 || $1 }) Link to comment Подели на овим сајтовима More sharing options...
Juanito Написано Јануар 27, 2015 Аутор Пријави Подели Написано Јануар 27, 2015 Мој нови блог пост на тему... https://medium.com/@ivicamil/swift-methods-are-functional-friendly-af78cf54b8ce Link to comment Подели на овим сајтовима More sharing options...
Juanito Написано Март 31, 2015 Аутор Пријави Подели Написано Март 31, 2015 Типичан ОО дизајн је дефинисање заједничког интерфејса (или апстрактне класе), кога онда конкретне класе имплементирају: interface IWoman { IEnumerable<Gossip> TalkToFriends(); Pleasure HaveSex(IMan partner); } class Ann : IWoman { IEnumerable<Gossip> TalkToFriends { // bla, bla } Pleasure HaveSex(IMan partner) { // having fun... } } Ово је супер кад све класе заиста имају идентичан интерфејс. Полиморфизам рулз, нема if-else и осталих примитивних конструкција. Али шта ако то није случај? Шта ако: 1) желим да имам заједнички интерфејс, али 2) важан ми је идентитет сваке класе? Желим да знам конкретно с којом класом радим, како бих позивао конкретне методе те класе. На пример, имам интерфејс IVisibilityState<T> и две класе које га имплементирају, Visible<T> и Hidden<T>. Желим да Visible<T> има јавно својство типа T, као и метод Hide(), који враћа Hidden<T>. Логично, Hidden<T> не треба да има јавно доступно својство типа Т. Такође би било бесмислено да овај тип има метод Hide(). Уместо тога, желим метод Show(), који враћа Visible<T>. Ово што сам горе написао није неопходно решење. Сасвим је прихватљиво дефинисати заједнички интерфејс, на следећи начин: public interface IVisibilityStateFirstVariant<T> { T Content { get; } IVisibilityState<T> Show(); IVisibilityState<T> Hide(); } Онда би конкретне класе Visible<T> и Hidden<T> (које могу да буду и приватне) имплементирале све ове методе и својство. Али се онда поставља питање шта да ради Visible<T> са Show() и шта да ради Hidden<T> са методом Hide() и својством Content? У овом случају би можда било скроз ОК да методи просто врате "this", али у већини реалних ситуација је неопходно корисника обавестити о грешци (бацање "изузетка" или слично). Content својство би могло просто да буде "null" у Hidden<T>. Проблем је што корисник ових класа о свему томе може да сазна само из документације и никако другачије. Уместо тога, желимо овако нешто: public interface IVisibilityState<T> { } public sealed class Visible<T> : IVisibilityState<T> { public T Content { get; private set; } public Visible(T content) { Content = content; } public Hidden<T> Hide() { return new Hidden<T>(Content); } } public sealed class Hidden<T> : IVisibilityState<T> { private T Content{ get; set; } public Hidden(T content) { Content = content; } public Visible<T> Show() { return new Visible<T>(Content); } } Две ствари овде боду очи. Прво, интерфејс је празан. Ем то није лепо, ем ће Visual Studio да нам испише упозорење о лошем дизајну. Друго, ако имамо листу IVisibilityState<T> објеката, једини начин да их разликујемо је downcasting. Фуј! Како решити оба проблема истовремено? Па тако што ће интерфејс дефинисати један елегантан начин да разликујемо две класе: public interface IVisibilityState<T> { void Handle(Action<Visible<T>> visible, Action<Hidden<T>> hidden); } Visual Studio ће нас и овде упозорити да нисмо добри дизајнери, јер користимо угњеждене генеричке параметре за јавни API, али кад може Мајкрософт, можемо и ми. У овом случају је оправдано пригушити то упозорење, јер су овакви API намењени за коришћење са ламбда изразима, те клијенти неће практично никад куцати ове параметре. Остало је још да у свакој класи имплементирамо интерфејс: public sealed class Visible<T> : IVisibilityState<T> { // ostatak koda je isti public void Handle(Action<Visible<T>> visible, Action<Hidden<T>> hidden) { visible(this); } } public sealed class Visible<T> : IVisibilityState<T> { // ostatak koda je isti public void Handle(Action<Visible<T>> visible, Action<Hidden<T>> hidden) { hidden(this); } } Сада можемо да разликујемо класе, без downcasting ругобе: class Test { void TestVisibilityState() { var visibleString = new Visible<String>("I'm here!"); visibleString.Handle( visible => Console.WriteLine("Content is visible: " + visible.Content), hidden => Console.WriteLine("Content is hidden.") ); } } Било би досадно кад бисмо ово разликовање морали стално да радимо. Уместо тога је боље користити специјализоване методе, као што ћу ускоро показати. Овом приступу се може (на први поглед) замерити то што интерфејс "зна" све класе које га имплементирају, те да та спрега спречава надградњу и проширивање. Сваки пут када додајете нову класу, морате да модификујете интерфејс, а то нарушаве све оне дивне ОО принципе и добре праксе. Али баш у томе и јесте фора. Ако дизајнирате систем који захтева надрградњу (UI библиотеке или слично), не бисте ни у лудилу овако радили. Ово је корисно када имате фиксиран и мали број класа, а желите да додајете функционалност (што можете чинити додајући методе у сам интерфејс, кроз екстензију). На пример: public static class VisibilityStateExtensions { public static IVisibilityState<U> TransformContent<T, U>(this IVisibilityState<T> state, Func<T, U> transform) { IVisibilityState<U> newState = null; state.Handle( visible => newState = new Visible<U>(transform(visible.Content)), hidden => newState = new Hidden<U>(transform(hidden.Show().Content)) ); return newState; } } Метод трандформише садржај, користећи дату функцију, али задржава контекст. Ако је објекат био невидљив, остаће невидљив. На пример: class Test { void TestVisibilityState() { var visibleString = new Visible<String>("I'm here!"); var invisibleString = new Hidden<String>("You can't see me!"); var visibleUpperCase = visibleString.TransformContent(content => content.ToUpper()); var invisibleUpperCase = invisibleString.TransformContent(content => content.ToUpper()); } } Као што видите, на крају смо ипак дошли до тога да класе имају заједнички интерфејс. Једино што су нам за то биле потребне функције вишег реда. односно делегати. Ова техника је јако слична пројектној мустри Visitor, само што је знатно лакша за клијенте. Уместо да клијент, за сваку нову функционалност, прави посебне класе, може користити једноставне ламбда изразе. Grizzly Adams је реаговао/ла на ово 1 Link to comment Подели на овим сајтовима More sharing options...
Grizzly Adams Написано Март 31, 2015 Пријави Подели Написано Март 31, 2015 Виђу врага... ΜΟΛΩΝ ΛΑΒΕ Link to comment Подели на овим сајтовима More sharing options...
Juanito Написано Март 31, 2015 Аутор Пријави Подели Написано Март 31, 2015 Виђу врага... Ајд, враже, гитаре ти, додај горе на почетку заграде после 'TalkToFriends'. Grizzly Adams је реаговао/ла на ово 1 Link to comment Подели на овим сајтовима More sharing options...
Anika Написано Март 31, 2015 Пријави Подели Написано Март 31, 2015 Виђу врага... Мушко-женски односи чак и овде. Unbelievable Juanito and Grizzly Adams је реаговао/ла на ово 2 "Ја сам сакупљач необичних ствари. Нека други сакупљају значке и марке. Ја сакупљам дане, часове и тренутке. " - Мирослав Антић Link to comment Подели на овим сајтовима More sharing options...
Juanito Написано Април 1, 2015 Аутор Пријави Подели Написано Април 1, 2015 Препорука за један сјајан курс: Functional Programming Principles in Scala by Martin Odersky Н И Н Е је реаговао/ла на ово 1 Link to comment Подели на овим сајтовима More sharing options...
Препоручена порука