All streams
Search
Write a publication
Pull to refresh
5
0
Александр Данилов @gen1lee

13 лет опыта коммерческой разработки

Send message

В моих примерах используется динамическое создание функций с замыканиями, и их возвращение из других функций. Это по вашему ПП?

В моих примерах используется динамическое создание функций с замыканиями, и их возвращение из других функций. Это по вашему ПП?

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

Они могли быть реализованы и как параметры функции без проблем. Команда реактор нашла еще более удобное решение, которое успешно используется.

В своей статье я четко даю понять что считаю ООП - наличие классов с наследованием и тп. А связывание данных и методов теми же замыканиями - не считаю. В Си классов нет, точка.

Если вы внимательно почитаете еще раз, то речь там идет о методах классов. И в них нет ничего хорошего.

То что есть в Го и то что вы привели на Расте - по сути метод интерфейса, и по моей терминологии и близко не является ООП. И плохого в этом только то, что 1) появляется два способа вызова функций, через точку и без 2) в популярных редакторах первый - с автокомплитом, второй без.

А вот Zig как раз по сути ООП-шный (мусор), если там есть методы, и методы конкретного типа а не интерфейса (полиморфизм подклассов).

Про термин с ФП и ПП - я в примерах использую динамическое создание функций, с замыканием - это по вашему ПП?

у вас в обоих примерах (кодеки и тулы) "синтаксис" более огромный

Речь про синтаксис языков из проблем ООП из статьи. И он в обоих примерах простейший.

В обоих примерах на C++ нет проблемы банана и макаки

Это потому что примеры погонялись именно так. Что если нужно написать кодек, фолбеки которого используют единое состояние? А если нужно, чтоб одна функция кодека работала как у кодека х, а другая как у кодека y?

При этом сделали такой же конструктор createState ... добавляются проблемы копипасты

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

Называть функциональным программированием программирование функциями это очень смешно.

Если я сделал все то же самое но без огромного синтаксиса популярных ООП языков, без проблемы банана и макаки, и других проблем из статьи - не кажется ли что так и нужно делать?

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

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

а в их общем предке

А если у них уже есть предки? Статью перечитайте.

Есть куча ООП языков .. го, например

Go как раз язык ФП.

Полиморфизм наследования - полное говно, статью почитайте еще раз.

ваш код вообще неподдерживаем  ... что вы будете делать, если

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

Зря я, конечно, упрощенный пример вам написал

За несколько попыток не смогли придумать ничего сложнореализуемого на ФП, и дальше не придумаете.

вы все-равно почти используете ООП

Если считать замыкания ООП то можно что угодно назвать ООП кроме чисто процедурного подхода. И даже его - ведь this с точкой по сути это подстановка первого аргумента.

Принципиальная разница - классы, которые:

  • являются результатом раннего связывания данных к конкретному типу. В моем примере функции существуют в несвязанном состоянии, и связываются с замыканием только во время выполнения. При желании их можно переиспользовать в любом другом типе в отличие от методов класса.

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

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

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

Многие минусы современного ООП на классах можете еще раз перечитать в статье.

const только для ссылки на state, сам state мутабельный, и соответственно fallback.

Не закрепить ли мне этот комментарий..

Код
type CollectState = {
  radius: number
  items: number[]
}

type AlignState = CollectState

type InstancerState = {
  items: string[]
}

type State = {
  activeTool?: string
  toolStates: Record<
    string,
    | ReturnType<typeof collect.createState>
    | ReturnType<typeof align.createState>
    | ReturnType<typeof instancer.createState>
  >
}

const collectItems = (items: number[], radius: number) => {
  for (let i = 0; i < radius; i++) {
    items.push(i)
  }
  console.log(`\tadded items in radius ${radius}`)
}

const collect = {
  createState: (): CollectState & {type: 'collect'} => ({items: [], radius: 0, type: 'collect'}),
  processPick: ({items, radius}: CollectState) => {
    console.log('From ToolCollectInRadius')
    collectItems(items, radius)
    console.log('\t', items.join(','))
  },
  update: (state: CollectState, i: number) => {
    state.radius += i
  },
}

const align = {
  ...collect,
  createState: (): AlignState & {type: 'align'} => ({items: [], radius: 0, type: 'align'}),
  processPick({items, radius}: AlignState) {
    console.log('From ToolAllign')
    collectItems(items, radius)
    console.log(`\talign ${items.length} items`)
  },
}

const instancer = {
  createState: (): InstancerState & {type: 'instancer'} => ({items: [], type: 'instancer'}),
  processPick({items}: InstancerState) {
    console.log('From ToolInstancer')
    items.forEach((item) => console.log(`\tspawn item: ${item}`))
  },
  update({items}: InstancerState, i: number) {
    items.push(i.toString())
  },
}

const state: State = {
  toolStates: {},
}

const processClick = () => {
  if (!state.activeTool) return

  const toolState = state.toolStates[state.activeTool]
  switch (toolState.type) {
    case 'align':
      return align.processPick(toolState)
    case 'collect':
      return collect.processPick(toolState)
    case 'instancer':
      return instancer.processPick(toolState)
  }
}

const updateTool = (i: number) => {
  if (!state.activeTool) return

  const toolState = state.toolStates[state.activeTool]
  switch (toolState.type) {
    case 'align':
      return align.update(toolState, i)
    case 'collect':
      return collect.update(toolState, i)
    case 'instancer':
      return instancer.update(toolState, i)
  }
}

state.toolStates.collectInRadius = collect.createState()
state.toolStates.align = align.createState()

state.activeTool = 'collectInRadius'
updateTool(8)
processClick()

state.activeTool = 'align'
updateTool(15)
processClick()

state.toolStates.instancer = instancer.createState()

state.activeTool = 'instancer'
updateTool(10)
processClick()

state.activeTool = 'collectInRadius'
processClick()

state.activeTool = 'instancer'
updateTool(100)
processClick()

Playground.
Даже без замыканий, чистый процедурный стиль, полностью типизирован. В полтора раза меньше строчек чем у вас, без классов.

На замыканиях еще проще и компактнее можно.

Код
type XxxState = {
  fallback: boolean
  fallbackState: YyyState
}
type YyyState = {}

const encodeXxx = (state: XxxState, frame: Frame) => {}
const encodeYyy = (state: YyyState, frame: Frame) => {}
const requestFrameYyy = (state: YyyState) => {}
const requestFrameXxx = (state: XxxState) => {
  if (state.fallback) {
    return requestFrameYyy(state.fallbackState)
  }
  // ...
}

const main = () => {
  const codecs = getCodecs()

  const encoders = codecs.map((encoding) => {
    switch (encoding) {
      case 'xxx': {
        const state: XxxState = {fallback: false, fallbackState: {}}
        return {
          encode: (frame: Frame) => encodeXxx(state, frame),
          requestFrame: () => requestFrameXxx(state),
        }
      }
      case 'yyy': {
        const state: YyyState = {}
        return {
          encode: (frame: Frame) => encodeYyy(state, frame),
          requestFrame: () => requestFrameYyy(state),
        }
      }
    }
  })

  while (true) {
    const event = getEvent()
    if (!event) {break}

    for (const {encode, requestFrame} of encoders) {
      switch (event.type) {
        case 'got-frame': {
          encode(event.frame)
          break
        }
        case 'requested-key-frame': {
          requestFrame()
          break
        }
      }
    }
  }
}

Здесь полный код, в отличие от вашего. Изменяемое состояние, разное у разных кодеков. У одного есть fallback.

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

Я же сказал, я НЕ понимаю что конкретно делает этот говнокод.

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

Нет, они даже статьи писали что после переписывания на TS с джавы работать все стало в 10 раз быстрее при меньшем количестве кода, и разработка быстрее при меньшем количестве разрабов.

Я думал

Думал но не сказал, молодец. Код выводит то же самое, если нужно состояние очевидно нужно было использовать состояние в выводе, чтобы потом не писать что ты думал?

код сразу же проспится если

Очередная демагогия "а вот если бы". Я предоставил код который куда проще и без классов. Могу предоставить еще хоть с состоянием, хоть с чем. Перепиши свой код так, чтоб работал "как ты думаешь", я скину аналог.

Лишний мусор только просьба удалить и сделать его минималистичным.

type Tool = 'collect-in-radius' | 'align' | 'instancer'

const collectInRadius = (x: number, y: number) => console.log('From ToolCollectInRadius')
const align = (x: number, y: number) => {
  collectInRadius(x, y)
  console.log('From ToolAlign')
}
const instancer = (x: number, y: number) => console.log('From ToolInstancer')

const tools: Record<Tool, (x: number, y: number) => void> = {
  'collect-in-radius': collectInRadius,
  align,
  instancer,
}

const processClick = (tool: Tool, x: number, y: number) => {
  tools[tool]?.(x, y)
}

const main = () => {
  processClick('collect-in-radius', 10, 10)
  processClick('align', 10, 10)
  processClick('instancer', 10, 10)
}

main()

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

Если нужна доп логика, допиши в вывод в консоль, либо тесты.

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Registered
Activity

Specialization

Frontend Developer, Mobile Application Developer
Lead