Отладка функционального кода в Scala - PullRequest
30 голосов
/ 25 ноября 2010

Отладка функционального кода определенно сложнее, чем отладка императивного кода. Смотрите обсуждения здесь , здесь и здесь . «Функциональная» отладка должна поддерживать проверку возвращаемого значения функций / замыканий / монад. Имеются ли у каких-либо отладчиков / IDE (планируется иметь) возможность проверки промежуточных возвращаемых значений?

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

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

Ответы [ 7 ]

33 голосов
/ 25 ноября 2010

Я думаю, что всем советуют разбить эту вещь на более управляемые куски, это лучший подход. Одним из способов отладки небольших выражений является кража функции 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)

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

11 голосов
/ 25 ноября 2010

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

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

4 голосов
/ 25 ноября 2010

Я знаю, что быть лаконичным - это очень хорошо, и я согласен с вами, что 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)

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

3 голосов
/ 25 ноября 2010

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

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

Конечно, YMMV.

2 голосов
/ 02 октября 2013

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

1 голос
/ 10 февраля 2019

Начиная с Scala 2.13, операция связывания tap, как упоминалось в Ответ Адама Рабунга , была включена в стандартную библиотеку и может использоваться для отладки при печатипромежуточные версии конвейера:

import scala.util.chaining._

scala> val ls = List(1,2,3).map(_ * 2).tap(println).map(_ * 2).tap(println)
List(2, 4, 6)
List(4, 8, 12)
ls: List[Int] = List(4, 8, 12)

Операция сцепления tap применяет побочный эффект (в данном случае println) к значению (в данном случае aList) при возврате исходного значения:

def нажмите [U] (f: (A) => U): A

1 голос
/ 29 марта 2016

Если у вас нет 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

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

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