Подтвердить что ты не робот

Отладка функционального кода в Scala

Функциональный код отладки определенно более сложный, чем отладочный императивный код. Здесь обсуждаются здесь, здесь и здесь. "Функциональная" отладка должна поддерживать проверку возвращаемого значения функций/замыканий/монад. Есть ли у каких-либо отладчиков /IDE возможность проверить промежуточные значения возврата?

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

val r=(ls filter (_>1) sort (_<_) zipWithIndex) filter {v=>(v._2)%2==0} map{_._1}
4b9b3361

Ответ 1

Я думаю, что каждый совет, чтобы разбить эту вещь на более управляемые куски, - лучший подход. Один трюк для отладки меньших выражений - это кража функции Ruby tap, как описано здесь. "tap" позволяет вам придерживаться выражения в середине такой цепи и, возможно, распечатывать некоторые значения отладки, например:

val ls = List(1,2,3).map(_ * 2)
                .tap(soFar => println("So far: " + soFar))
                .map(_ * 2)
println(ls)

Это напечатает:

До сих пор: список (2, 4, 6)
Список (4, 8, 12)

Это помогает мне время от времени.

Ответ 2

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

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

Ответ 3

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

val noZeroLs = ls.filter(_>1)
val sortedLs = noZeroLs.sort(_<_)
val indexedNoZeroLs = sortedLs.zipWithIndex
val everySecondIndexedL = indexedNoZeroLs.filter(v => (v._2) % 2 == 0)
val everySecondL = everySecondIndexedL.map(_._1)

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

Ответ 4

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

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

Конечно, YMMV.

Ответ 5

Это полезно при попытке отладки собственного кода, но во время отладки scala lang или других libs вы не можете изменить код: (

Ответ 6

Если у вас нет IDE, вы все равно можете использовать этот инструмент, который я написал:

https://github.com/JohnReedLOL/scala-trace-debug

Чтобы распечатать промежуточные значения, вы можете взять этот пример:

val result=(lists.filter(_>1).sort(_<_).zipWithIndex).filter{v=>(v._2)%2==0}.map{_._1}

И добавьте в него следующие трассы:

import scala.trace.implicitlyTraceable
val result=(lists.filter(_>1).out.sort(_<_).println.zipWithIndex).filter{v=>(v._2)%2==0}.out.map{_._1}.out

Неявное преобразование позволяет печатать.